用python中的lambda表达式在循环内生成函数
如果我列出两个函数列表:用python中的lambda表达式在循环内生成函数,python,lambda,scope,Python,Lambda,Scope,如果我列出两个函数列表: def makeFun(i): return lambda: i a = [makeFun(i) for i in range(10)] b = [lambda: i for i in range(10)] 为什么列表a和b的行为不符合保存方式 例如: >>> a[2]() 2 >>> b[2]() 9 python中的lambda共享在其中创建它们的变量范围。在第一种情况下,lambda的范围是makeFun。在第二种
def makeFun(i):
return lambda: i
a = [makeFun(i) for i in range(10)]
b = [lambda: i for i in range(10)]
为什么列表a
和b
的行为不符合保存方式
例如:
>>> a[2]()
2
>>> b[2]()
9
python中的lambda共享在其中创建它们的变量范围。在第一种情况下,lambda的范围是makeFun。在第二种情况下,它是全局
i
,它是9,因为它是循环的剩余部分
这就是我对它的理解…从技术上讲,lambda表达式在全局范围内可见的
I
上是闭合的,最后设置为9。这与所有10个lambda中引用的i
相同。比如说,
i = 13
print b[3]()
在
makeFun
函数中,lambda在调用函数时定义的i
上关闭。这是十种不同的i
s.很好的捕捉。列表中的lambda每次都看到相同的本地i
您可以将其改写为:
a = []
for i in range(10):
a.append(makefun(i))
b = []
for i in range(10):
b.append(lambda: i)
具有相同的结果。一组函数(a)对传递的参数进行操作,另一组函数(b)对全局变量进行操作,然后将全局变量设置为9。检查拆卸:
>>> import dis
>>> dis.dis(a[2])
1 0 LOAD_DEREF 0 (i)
3 RETURN_VALUE
>>> dis.dis(b[2])
1 0 LOAD_GLOBAL 0 (i)
3 RETURN_VALUE
>>>
正如其他人所说,范围界定是一个问题。请注意,可以通过向lambda表达式添加额外参数并为其指定默认值来解决此问题:
>> def makeFun(i): return lambda: i
...
>>> a = [makeFun(i) for i in range(10)]
>>> b = [lambda: i for i in range(10)]
>>> c = [lambda i=i: i for i in range(10)] # <-- Observe the use of i=i
>>> a[2](), b[2](), c[2]()
(2, 9, 2)
>def makeFun(i):返回lambda:i
...
>>>a=[makeFun(i)代表范围(10)内的i]
>>>b=[lambda:i表示范围(10)内的i]
>>>c=[lambda i=i:i代表范围(10)]#>>a[2](),b[2](),c[2]()
(2, 9, 2)
结果是,i
现在被明确地放在一个限定于lambda
表达式的范围内。以增加一些清晰性(至少在我看来)
a使用makeFun(i),这是一个带有参数的函数
b使用lambda:i,它是一个没有参数的函数。它使用的i与以前的i非常不同
要使a和b相等,我们可以使两个函数都不使用参数:
def makeFun(): return lambda: i
a = [makeFun() for i in range(10)]
b = [lambda: i for i in range(10)]
现在两个函数都使用全局i
>>> a[2]()
9
>>> b[2]()
9
>>> i=13
>>> a[2]()
13
>>> b[2]()
13
或者(更有用)使两者都使用一个参数:
def makeFun(x): return lambda: x
a = [makeFun(i) for i in range(10)]
b = [lambda x=i: x for i in range(10)]
我故意用x来改变I,其中变量是局部的。
现在:
成功 我不知道你想说什么。在您的示例中,
b[2]()
也返回9;不是说a和b是一样的。我认为拼写出的for
循环将更容易看到I
的来源。好了,现在这对我来说是有意义的。谢谢从Python3开始,列表理解现在有了自己的作用域。所以改变它的值不会改变print(b[3]())的输出。有关列表理解行为的更多详细信息,请参见
def makeFun(x): return lambda: x
a = [makeFun(i) for i in range(10)]
b = [lambda x=i: x for i in range(10)]
>>> a[2]()
2
>>> b[2]()
2