【问题标题】:Generating a list of functions in python在python中生成函数列表
【发布时间】:2011-05-24 11:32:53
【问题描述】:

我有以下生成匿名函数列表的 python 代码:

basis = [ (lambda x: n*x) for n in [0, 1, 2] ]     
print basis[0](1)

我本来希望它等同于

basis = [ (lambda x: 0*x), (lambda x: 1*x), (lambda x: 2*x) ]
print basis[0](1)

然而,虽然第二个 sn-p 打印出 0,这是我所期望的,但第一个打印出 2。第一个 sn-p 代码有什么问题,为什么它的行为不符合预期?

【问题讨论】:

标签: python list lambda


【解决方案1】:

您可以使用默认参数在 n 上创建闭包

>>> basis = [ (lambda x,n=n: n*x) for n in [0, 1, 2] ]     
>>> print basis[0](1)
0

【讨论】:

  • 这种技术经常用于将方法作为回调函数传递。它看起来像register_event_handler(event=evt, callback=lambda cbVar1, cbVar2, s=self: s.handle_evt(cbVar1, cbVar2))
【解决方案2】:

因为它是“按名称传递”。

lambda运行时,执行n*xx绑定1(是参数),n在环境中查找(是现在 2)。所以,结果是 2。

【讨论】:

  • 这并不能解释为什么当我们使用生成器理解时会表现出相同的行为,它不会“泄漏”nlocals() 中。 AFAICT,n 的值是从 lambda 的 .func_closure 中查找的,而且当 n 不再存在时,它是如何神奇地指代上次提到的 n 完全不清楚。
【解决方案3】:

问题在于,在第一个示例中,每个 lambda 都绑定到相同的 n —— 换句话说,它捕获的是变量,而不是变量的值。由于 n 在循环结束时的值为 2,因此每个 lambda 都将值 2 用于 n

显然你可以使用默认参数来解决这个问题:

basis = [ (lambda x,n=n: n*x) for n in [0, 1, 2] ]
print basis[0](1) 

由于默认参数值是常量,因此每次循环都会对n=n 右侧的n 求值,从而为您提供一个新的捕获值。

【讨论】:

  • 关于如何解决这个问题的任何建议?
  • celil:我编辑了我的答案以提供建议的修复,但它不是很优雅。
  • 默认参数可能并不优雅,但它们比我以前做的显式部分评估要容易得多:basis = [(lambda n:(lambda x: n * x))(n) for n in range(3)]。 :/ 默认参数不是不可变意义上的“常量”,但它确实被评估并绑定到 lambda 的.func_defaults。闭包参数也是如此,但显然是以一种更神奇的方式......
  • Karl:“常量”是指表达式被计算一次并且永远不会改变。当然,该表达式的计算结果为引用,然后您可以更改它所引用的对象,但您不能更改引用本身。
【解决方案4】:

我想帮助理解 Karl Knechtel 的评论(2010 年 12 月 13 日 7:32)。下面的代码展示了如何使用生成器,原始的 lambda 定义给出了预期的结果,但它没有使用列表或元组:

>>> #GENERATOR
... basis = ( (lambda x: n*x) for n in [0, 1, 2] )  
>>> print(type(basis))
<type 'generator'>
>>> basis = ( (lambda x: n*x) for n in [0, 1, 2] ) 
>>> print([x(3) for x in basis])
[0, 3, 6]
>>> #TUPLE
... basis = tuple( (lambda x: n*x) for n in [0, 1, 2] )
>>> print(type(basis))
<type 'tuple'>
>>> print([x(3) for x in basis])
[6, 6, 6]
>>> #LIST
... basis = list( (lambda x: n*x) for n in [0, 1, 2] )
>>> print(type(basis))
<type 'list'>
>>> print([x(3) for x in basis])
[6, 6, 6]
>>> #CORRECTED LIST
... basis = list( (lambda x, n=n: n*x) for n in [0, 1, 2] )
>>> print(type(basis))
<type 'list'>
>>> print([x(3) for x in basis])
[0, 3, 6]

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2010-12-25
    • 2021-04-26
    • 1970-01-01
    • 1970-01-01
    • 2021-02-23
    • 2014-03-17
    • 1970-01-01
    相关资源
    最近更新 更多