Python 在回路中连接PyQt4中的插槽和信号

Python 在回路中连接PyQt4中的插槽和信号,python,pyqt4,signals-slots,Python,Pyqt4,Signals Slots,我正在尝试用PyQt4构建一个计算器,但是连接按钮的“clicked()”信号并没有按预期工作。 我正在为for循环中的数字创建按钮,之后我会尝试连接它们 def __init__(self): for i in range(0,10): self._numberButtons += [QPushButton(str(i), self)] self.connect(self._numberButtons[i], SIGNAL('clicked()'), l

我正在尝试用PyQt4构建一个计算器,但是连接按钮的“clicked()”信号并没有按预期工作。 我正在为for循环中的数字创建按钮,之后我会尝试连接它们

def __init__(self):
    for i in range(0,10):
        self._numberButtons += [QPushButton(str(i), self)]
        self.connect(self._numberButtons[i], SIGNAL('clicked()'), lambda : self._number(i))

def _number(self, x):
    print(x)
当我点击按钮时,所有的按钮都会打印出“9”。
为什么会这样?我该如何解决这个问题?

这就是Python中定义作用域、名称查找和闭包的方式

Python仅通过赋值和函数的参数列表在名称空间中引入新绑定<因此,code>i实际上不是在
lambda
的名称空间中定义的,而是在
的名称空间中定义的。lambda中
i
的名称查找结果是在
\uuuu init\uuuu()
的命名空间中,其中
i
最终绑定到
9
。这被称为“关闭”

您可以通过将
i
作为带有默认值的关键字参数传递,来解决这些不太直观(但定义良好)的语义。如上所述,参数列表中的名称在本地名称空间中引入了新的绑定,因此
lambda
中的
i
将独立于
中的
i

一个更具可读性、不那么神奇的选择是
functools.partial

self._numberButtons[i].clicked.connect(partial(self._number, i))

我在这里使用新型的信号和插槽语法只是为了方便起见,旧式语法的工作原理是一样的。

您正在创建闭包。闭包真正捕获的是变量,而不是变量的值。在
\uuu init\uuu
的末尾,
i
范围(0,10)
的最后一个元素,即
9
。在此范围内创建的所有lambda都引用此
i
,只有在调用它们时,它们才会在被调用时获得
i
的值(但是,单独调用
\uuu init\uuu
创建lambda引用单独的变量!)

有两种常用的方法可以避免这种情况:

  • 使用默认参数:
    lambda i=i:self.\u编号(i)
    。这是因为默认参数在函数定义时绑定了一个值
  • 定义助手函数
    helper=lambda i:(lambda:self.\u number(i))
    并在循环中使用
    helper(i)
    。这是因为“外部”
    i
    是在绑定
    i
    时计算的,正如前面提到的,在下一次调用
    helper
    时创建的下一个闭包将引用不同的变量

  • 使用
    Qt
    方式,改用。

    谢谢。我会选择部分解决方案,请不要<代码> QSaleMalPoper-<代码>是C++ 98的残余,它没有lambda或部分函数,并且这种处理是必要的邪恶。然而,在Python中,
    QSignalMapper
    functools.partial()
    或lambda相比只是多余的,而且非常臃肿。使用
    partial()
    lambda
    的解决方案比
    QSignalMapper
    更短、更优雅。哦,这是关于选择的问题,我会选择
    QSignalMapper
    来实现Qt/C++兼容性。当然,这是关于选择的问题,但我根本不理解您的选择,我也无法理解。我使用Python来QT应用程序,因为它不是C++,如果我放弃所有Python的特殊特性来维护或达到C++兼容性,我也可以使用C++。
    self._numberButtons[i].clicked.connect(partial(self._number, i))