我在Python的官方手册中读了一句话。
如果在函数内定义的lambda或def嵌套在循环内,并且>嵌套函数引用该循环所更改的封闭范围变量,则循环内生成的所有函数将具有相同的值-该值>所引用的变量在上一次循环迭代中具有。在这种情况下,您必须>仍然使用默认值来保存变量的当前值。
关于脚本:
def makeActions():
acts=[]
for i in range(5):
acts.append(lambda x: i**x)
return acts
acts=makeActions()
print(acts)
我知道问题的外观以及解决方法。但是我就是无法理解原因。 请看看我的解释:
def makeActions():
acts=[]
for i in range(5):
acts.append(lambda x: i**x) #step2-step6: each lambda would be stored
#in the list, however, with its own "i"
return acts
acts=makeActions() #step1: when the function is called, the list would be
#created. And lambda would be called as well.
print(acts)
我的观点是,在每次迭代中,即使未给出参数x也会调用每个lambda。因此,在“行为”列表中,每个元素(即每个lambda)具有不同的“ i”。
我知道我错了,但是你能告诉我为什么吗? 谢谢!
答案 0 :(得分:4)
为了进一步说明这个问题,对i
的引用在lambda中没有改变-您看到i
的每次迭代都会生成一个新对象:
def makeActions():
acts=[]
for i in range(5):
print('i:', i, 'object id:', id(i))
acts.append(lambda: id(i))
print('final i:', i, 'object id:', id(i))
return acts
acts=makeActions()
print([fn() for fn in acts])
返回
i: 0 object id: 140418781832928
i: 1 object id: 140418781832960
i: 2 object id: 140418781832992
i: 3 object id: 140418781833024
i: 4 object id: 140418781833056
final i: 4 object id: 140418781833056
[140418781833056, 140418781833056, 140418781833056, 140418781833056, 140418781833056]
i
的引用始终指向其最后分配的对象,因此i
的ID在最后一次更改后将不会更改。
如果要在创建lambda时保留值,则需要在所需的位置和时间“捕获”引用的值,例如,可以将lambda创建委托给一个函数:>
def delegated(d):
return lambda: id(d)
def makeDelegatedActions():
acts=[]
for i in range(5):
print('i:', i, 'object id:', id(i))
acts.append(delegated(i))
print('final i:', i, 'object id:', id(i))
return acts
acts=makeDelegatedActions()
print([fn() for fn in acts])
返回哪个
i: 0 object id: 140418781832928
i: 1 object id: 140418781832960
i: 2 object id: 140418781832992
i: 3 object id: 140418781833024
i: 4 object id: 140418781833056
final i: 4 object id: 140418781833056
[140418781832928, 140418781832960, 140418781832992, 140418781833024, 140418781833056]
答案 1 :(得分:2)
每个lambda只会知道从其封闭范围(即i
调用)中提取makeActions
。他们实际上并没有这么做,直到他们自称。 i
是同一作用域内的相同名称,它恰好在循环期间采用不同的值,并且仅在循环完成后保留最后一个值。因此,lambda最终创建了5个相同的函数。
可行的变体形式
def expgen(n):
def myexp(x):
return n**x
return myexp
acts = [expgen(i) for i in range(5)]
在这种情况下,获取n
的范围是expgen
的范围,它在列表理解中具有不同的调用,并且每个函数将独立工作。