Fre*_*ney 2 python lambda binding
以下代码将值转换为函数:
>>> a = map(lambda(x): lambda: x, [1, 2])
>>> [func() for func in a]
[1, 2]
Run Code Online (Sandbox Code Playgroud)
但是,以下代码段失败:
>>> a = [lambda: x for x in [1, 2]]
>>> [func() for func in a]
[2, 2]
Run Code Online (Sandbox Code Playgroud)
这种像差是参数名称绑定工件吗?
这是关于范围的.函数定义新范围; 列表推导的迭代,如for循环和其他块语句,不会.
Python编程常见问题解答为什么在具有不同值的循环中定义的lambdas都返回相同的结果?从高层次解释这一点.我将尝试不同的解释,略读高层,然后深入潜水.
您的第一个版本是调用lambda: x为每个元素返回的函数.因为函数定义了新的作用域,所以每个返回的函数都有自己独立的x.
你的第二个版本只是lambda: x为每个元素定义一个.因为您在同一范围内执行此操作,所以每个此类已定义的函数都具有相同的功能x.事实上,因为它x是在全球范围内发现的,所以x每个人都拥有全球范围,正如您所看到的那样:
>>> b = [lambda: x for x in [1, 2]]
>>> x = 20
>>> [func() for func in b]
[20, 20]
Run Code Online (Sandbox Code Playgroud)
您可以通过定义和调用函数来修复此问题,使第二个版本等同于第一个版本,或者以通常的方式解决它,例如"默认参数hack":
>>> c = [lambda x=x: x for x in [1, 2]]
>>> [func() for func in c]
[1, 2]
Run Code Online (Sandbox Code Playgroud)
值得查看存储在函数对象中的内容以查看差异:
>>> a = map(lambda(x): lambda: x, [1, 2])
>>> [f.__closure__ for f in a]
[(<cell at 0x106523e50: int object at 0x7fb6a3c10298>,),
(<cell at 0x106523fa0: int object at 0x7fb6a3c10280>,)]
>>> [f.__code__.co_freevars for f in a]
(('x',), ('x',))
Run Code Online (Sandbox Code Playgroud)
所以,在这里,每个函数都是一个带有单个单元格的闭包,每个单元格都有一个名称x,但每个单元格都有一个对不同int对象的引用(x每次通过循环都绑定了该值).
>>> b = [lambda: x for x in [1, 2]]
>>> [f.__closure__ for f in b]
[None, None]
>>> [f.__code__.co_freevars for f in b]
((), ())
>>> [f.__code__.co_names for f in b]
(('x',), ('x',))
Run Code Online (Sandbox Code Playgroud)
所以这些根本不是闭包,只是引用全局变量的函数.
>>> c = [lambda x=x: x for x in [1, 2]]
>>> [f.__closure__ for f in b]
[None, None]
>>> [f.__code__.co_freevars for f in c]
((), ())
>>> [f.__code__.co_names for f in c]
((), ())
>>> [f.__code__.co_varnames for f in c]
(('x',), ('x',))
>>> [f.__defaults__ for f in c]
((1,), (2,))
Run Code Online (Sandbox Code Playgroud)
这里没有闭包,也没有全局变量; 我们有一个局部变量绑定到第一个参数,其默认值分别为1或2.由于您在func没有参数的情况下调用,因此您将获得默认值.
或者,您可以查看反汇编:
>>> dis.dis(a[0])
1 0 LOAD_DEREF 0 (x)
3 RETURN_VALUE
>>> dis.dis(b[0])
1 0 LOAD_GLOBAL 0 (x)
3 RETURN_VALUE
>>> dis.dis(c[0])
1 0 LOAD_FAST 0 (x)
3 RETURN_VALUE
Run Code Online (Sandbox Code Playgroud)
但我怀疑有太多人知道Python字节码,但不知道函数的可检查值,所以...这可能没有多大帮助.
最后,这可能更容易思考 - 也许是阅读 - 如果你将函数定义函数移到行外,并使用def而不是lambda.
>>> def make_function(x):
... def function():
... return x
... return function
>>> a = map(make_function, [1, 2])
>>> b = [make_function(x) for x in [1, 2]]
Run Code Online (Sandbox Code Playgroud)
现在,这两个a和b正在做的,它返回一个同样的呼吁的事情,函数功能,而且也没什么好困惑.