变量如何在lambda函数中工作?

我试图用一个lambda函数和python 3.6中的一些PyQt5 QPushButtons将9个不同的按钮连接到一个处理程序。 如果我使用整数分配它们,所有工作都正常。 但是,如果我尝试使用一个列表和一个循环,他们都被分配到一个按钮数为10.我不明白为什么,因为我会认为我的任务是整数值,我的变量超出范围。 很显然,这里有一些我不明白的事情。 任何人都可以解释这段代码的行为吗?

    self.buttonList = [ self.sq1Button,
                        self.sq2Button,
                        self.sq3Button,
                        self.sq4Button,
                        self.sq5Button,
                        self.sq6Button,
                        self.sq7Button,
                        self.sq8Button,
                        self.sq9Button]
    buttonNumber = 1
    for button in self.buttonList:
        button.clicked.connect(lambda: self.squareButtonHandler(buttonNumber))
        buttonNumber += 1

当python执行一个函数时,它会创建一个名称空间来存放局部变量。 拉姆达在

button.clicked.connect(lambda: self.squareButtonHandler(buttonNumber))

是一个内部函数,它包含对外部作用域中的buttonNumber的引用。 当你将该lambda传递给button.clicked.connect ,python必须以某种方式记住该引用。 它通过将外部范围的上下文添加到它创建并传递来connect的函数对象来实现。 你连接的所有按钮的函数对象引用相同的外部上下文,这意味着当函数退出时,它们都将看到buttonNumber任何内容。

这是一个显示您的问题的运行示例

def buttonHandler(num):
    print('button', num)

def try_lambda():
    handlers = []
    for num in range(5):
        handlers.append(lambda: buttonHandler(num))
    return handlers

print("test 1")
for handler in try_lambda():
    handler()

它产生

test 1
button 4
button 4
button 4
button 4
button 4

是的,这是问题所在。 让我们看看我们通过查看函数对象的闭包创建的函数对象

print("test 2")
for handler in try_lambda():
    handler()
    print(handler, handler.__closure__)

表明

test 2
button 4
<function try_lambda.<locals>.<lambda> at 0x7f66e34a9d08> (<cell at 0x7f66e49fb3a8: int object at 0xa68aa0>,)
button 4
<function try_lambda.<locals>.<lambda> at 0x7f66e34a9d90> (<cell at 0x7f66e49fb3a8: int object at 0xa68aa0>,)
button 4
<function try_lambda.<locals>.<lambda> at 0x7f66e34a9e18> (<cell at 0x7f66e49fb3a8: int object at 0xa68aa0>,)
button 4
<function try_lambda.<locals>.<lambda> at 0x7f66e34a9ea0> (<cell at 0x7f66e49fb3a8: int object at 0xa68aa0>,)
button 4
<function try_lambda.<locals>.<lambda> at 0x7f66e349a048> (<cell at 0x7f66e49fb3a8: int object at 0xa68aa0>,)

有趣。 我们得到了4个不同的函数对象(0x7f66e34a9d08等),但是一个单元格拥有我们想要的变量0x7f66e49fb3a8。 这就是为什么他们都看到相同的号码 - 他们都使用外部函数的局部变量保存的单元格。

就你而言, partial是更好的选择。 它使用变量的当前值创建一个函数,并按您的需要工作。

import functools

def try_partial():
    handlers = []
    for num in range(5):
        handlers.append(functools.partial(buttonHandler, num))
    return handlers

print("test 3")
for handler in try_partial():
    handler()

它产生

test 3
button 0
button 1
button 2
button 3
button 4

我曾经有同样的问题,这对我有帮助。 你基本上需要做的就是将点击处理程序移动到一个单独的函数中,并使用循环内的buttonNumber调用该函数。 这可能是由于闭包的工作原理和/或每次循环运行时需要一个新的buttonNumber 。 我仍然不明白确切的原因,所以如果有人,请评论/编辑。

链接地址: http://www.djcxy.com/p/51259.html

上一篇: How do variable work inside a lambda function?

下一篇: Is there a way to modify datetime object in python?