我正在编写一些科学的python代码。在代码中的某个点上,我需要接受一个可调用对象列表(existing_functions
),然后通过在原始对象中注入一个变量变化来生成另一个可调用对象列表(changed_functions
)。 '__call__
个方法。在我看来,以下内容似乎是一个很好的抽象。
import numpy as np
changed_functions = [lambda x: f(np.exp(x)) for f in existing_functions]
我担心这里会滥用lambda
。因此,我在循环和生成器表达式中运行了一些涉及lambda
的简单测试。这些简单的测试产生了令人困惑的结果,其中似乎涉及我无法解读的可变范围或通过引用问题。
下面的代码片段尝试了四种不同的方法,以使用lambda
定义匿名函数列表。如我所愿,只有第一种方法(带有列表文字)可以工作。
# attempt 1: use a list literal
funcs = [lambda: 1, lambda: 2]
print(funcs[0]()) # 1
print(funcs[1]()) # 2
# attempt 2: much closer to my actual use-case.
gunks = [lambda: i for i in [1,2]]
print(gunks[0]()) # 2, but I expect 1.
print(gunks[1]()) # 2, as expected.
# attempt 3: an ugly version of my use case, which I thought might work.
hunks = [(lambda: i,)[0] for i in [1,2]]
print(hunks[0]()) # 2, but I expect 1.
print(hunks[1]()) # 2, as expected.
# attempt 4: another ugly version of my use case. "WE NEED MORE LAMBDA."
yunks = [(lambda: lambda: i)() for i in [1,2]]
print(yunks[0]()) # 2, but I expect 1.
print(yunks[1]()) # 2, as expected.
我希望有人可以向我解释,与列表文字相比,摘要中的最后三种方法为什么总会产生如此不同的结果。
答案 0 :(得分:3)
您将其与i
绑定,但是由于以后更改了i
,它将在范围内查找具有该名称的变量,从而选择您为该{ {1}}。
您可以通过在lambda函数中添加一个额外的参数来解决此问题:
i
或者,如果您不想这样做,可以在此处使用currying [wiki]:
changed_functions = [lambda i=i: i for i in [1,2]]
或者我们可以使用显式函数:
changed_functions = [(lambda j: lambda: j)(i) for i in [1, 2]]
因此,我们在这里创建一个lambda表达式,该表达式接受变量def function_maker(j):
return lambda: j
changed_functions = [function_maker(i) for i in [1, 2]]
并返回一个lambda表达式,该表达式返回该i
。然后,我们将i
与我们的i
值绑定,从而在更局部的范围内构造一个i
。
对于两者,然后返回:
i