看看这段代码。我正在创建3个lambda函数列表(存储在变量plus_n, plus_n_和plus_n__中)。它们应该是完全一样的。然而,只有plus_n_显示了预期的行为。
MAX=5
plus_n=[lambda x: x+i for i in range(MAX)]
plus_n_=[]
for i in range(MAX):
plus_n_.append(lambda x: x+i)
def all_plus_n():
plus_ns=[]
for i in range(MAX):
plus_ns.append(lambda x: x+i)
return plus_ns
plus_n__=all_plus_n()
for i in range(len(plus_n)):
print('plus_n[{}]({})={}'.format(i,3,plus_n[i](3)))
print('plus_n_[{}]({})={}'.format(i,3,plus_n_[i](3)))
print('plus_n__[{}]({})={}'.format(i,3,plus_n__[i](3)))
print()
输出:
plus_n[0](3)=7
plus_n_[0](3)=3
plus_n__[0](3)=7
plus_n[1](3)=7
plus_n_[1](3)=4
plus_n__[1](3)=7
plus_n[2](3)=7
plus_n_[2](3)=5
plus_n__[2](3)=7
plus_n[3](3)=7
plus_n_[3](3)=6
plus_n__[3](3)=7
plus_n[4](3)=7
plus_n_[4](3)=7
plus_n__[4](3)=7
看,完全相同的代码给出不同的结果,如果它是在一个函数或一个全面的列表…
那么,这三种方法之间的区别是什么?发生了什么?如果我想在多个函数中使用这个变量,我是否必须将它用作全局变量?因为似乎我不能使用函数来获取变量值…
提前谢谢。
这是通常问题的一个有趣的变体。通常,plus_n_
版本也不能工作,但是您碰巧在代码末尾重用了i
作为测试的迭代变量。由于plus_n_
捕获全局i
,并且测试循环也设置全局i
,因此从plus_n_
检索的lambda每次通过循环使用正确的值-即使它在i
上晚绑定。列表推导式有自己的作用域i
,计算后的值为4
(之后不会改变);函数中的循环也是如此。
在Python中绑定函数参数的干净,显式,简单的方法是来自标准库的functools.partial
:
from functools import partial
MAX = 5
# Or we could use `operator.add`
def add(i, x):
return i + x
# Works as expected, whatever happens to `i` later in any scope
plus_n = [partial(add, i) for i in range(MAX)]
这样,每次调用partial
都会产生一个可调用对象,该对象将自己的值绑定到i
,而不是延迟绑定到名称i
:
for j in plus_n:
i = {"this isn't even a valid operand": 'lol'} # utterly irrelevant
print(plus_n[j](3))
但是,请注意,要绑定的参数需要位于开头的。
解决特定示例问题的另一种方法是依赖于绑定方法调用:
plus_n = [i.__add__ for i in range(MAX)]
你也可以手卷你自己的咖喱,但为什么要重新发明轮子呢?
最后,可以使用lambdas的默认参数来绑定参数——尽管我非常不喜欢这种方法,因为它滥用了导致另一个常见问题的行为,并且暗示存在一个可以被覆盖但不是为它设计的参数:
plus_n = [lambda x, i=i: x+i for i in range(MAX)]