Python lambda list:函数中列表推导、for和for的不同行为



看看这段代码。我正在创建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)]