Python 闭包,默认参数与 functools.partial 的解决方案不一致?

3 投票
1 回答
681 浏览
提问于 2025-04-17 05:45

我正在用Python 3.2和PyQt做一个小游戏。我需要在菜单中插入一些操作,这些操作几乎是一样的,只是参数不同。我想到了用lambda表达式来实现,但结果发现所有的操作都用了同一个参数。

这其实是一个闭包的问题,我根据这个帖子解决了它。不过,有一个提议的解决方案(使用默认参数)本该和另一个方案等效,但它却不管用。当我用print函数做了一个小测试时,发现这两个方案的结果是一样的。

我想搞明白为什么在这种情况下它们的表现不同。是connect方法影响了它吗?这可能和Python的作用域有关。这是我正在做的代码片段(我省略了给操作命名和设置文本的部分):

cardsOptions = [15, 30, 45, 50, 55, 60, 10]
self.startActions = []
lambdas = []
for co in cardsOptions:
    action = QtGui.QAction(MainWindow)
    self.menuNewGame.addAction(action)
    # This works
    # action.triggered.connect(partial(self.StartGame, 8, co))

    lamb = (lambda a = co: self.StartGame(8, a))
    lambdas.append(lamb)

    # This doesn't work, when StartGame is called it gets arguments 8, false
    action.triggered.connect(lamb)
    self.startActions.append(action)

# This proves that closure was done ok, and it saved all co values
[m() for m in lambdas]

让我最惊讶的是,它把false作为第二个参数传递,就好像它在评估a = co一样。那么,使用默认参数的闭包和使用部分应用有什么不同,为什么会这样工作呢?

1 个回答

4

但是如果你写:

[m(False) for m in lambdas]

这会把参数8和False传给StartGame。

Qt可能在调用你的lambda函数时传入了False这个参数,所以lambda函数里的默认参数根本没用上。

不过你的想法很好。试试下面这个:

lamb = (lambda a, real_co = co : self.StartGame(8, real_co))

现在False就被忽略了,真正的real_co值会被使用。

撰写回答