将值转换为函数

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)

这种像差是参数名称绑定工件吗?

aba*_*ert 5

这是关于范围的.函数定义新范围; 列表推导的迭代,如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)

现在,这两个ab正在做的,它返回一个同样的呼吁的事情,函数功能,而且也没什么好困惑.