动态函数创建和函数体评估

时间:2015-05-06 19:09:26

标签: python function dynamic

考虑以下Python 3指令

res = []
for a in range(3) :
    res.append(lambda n : n + a)

旨在构建包含resres[0]res[1]三个函数的res[2]列表,以便res[i](n)返回n + i,适用于i中的所有[0, 1, 2]

然而,获得

>>> res[0](0)
2

而不是

>>> res[0](0)
0

一个人也有

>>> res[1](2)
4

此行为的解释是,在示例的任何动态生成的匿名函数的正文中的表达式n + a中,在创建函数时不评估符号a。评估在for语句的退出处执行,解释了为什么所有函数res[0]res[1]res[2]都返回其参数的值加2è (because a {{1 } range(3)browses 2`是它的最后一个值。)

请注意,问题不在于我们使用匿名函数。的确,说明

and

导致相同的行为。

另请注意,我们可以使用Python的res = [] for a in range(3) : def f(n) : return n + a res.append(f) 函数来实现上述目标:

eval

诀窍在于res = [] for a in range(3) : s = "lambda n : n + %s" %(a) res.append(eval(s)) 的动态值被考虑用于创建a的每个函数。

现在我的问题是

  1. 这是一个错误还是一个功能?
  2. 还有另一种不通过res来获取预期行为的方法吗?

5 个答案:

答案 0 :(得分:3)

由于closure动态函数引用变量a并使用它在执行时for循环结束时的最终值。

您可以通过使其成为具有默认值的函数参数来防止这种情况,因此不需要在调用时提供它。定义每个动态函数时a的值将成为使用的值。这就是我的意思:

res = []
for a in range(3) :
    res.append(lambda n, a=a: n + a)

print(res[0](0))  # -> 0
print(res[1](2))  # -> 3

答案 1 :(得分:2)

我不能说#1 ...除了说我确信这是有意和理想的有意和可取的行为

但WRT#2

res = []
for a in range(3) :
    res.append(lambda n,b=a : n + b)

res[0](0)

这将在for循环内(而不是退出后)将a评估为默认的第二个参数...

粗略的

使它开放到类似

的东西
res[0](0,8)

答案 2 :(得分:0)

如果你真的不想在lambda函数中使用两个参数,那么就有一条出路,虽然不是很漂亮:

res = []
for a in range(3):
    curr_a = str(a)
    exec('a' + curr_a +'=' + curr_a)
    exec('func= lambda x : x + a' + curr_a)
    res.append(func)
    print 'res=',res[a](0)

这会在每次迭代时创建不同的变量a0a1a2 ..结束a在最后被覆盖的问题。 产生:

>>> res[0](0)
0

IMO,这不是一个好的方法,对于range(k) k大{{1}}会让你最终得到大量未使用的变量。

答案 3 :(得分:0)

如果您不想使用lambda

,这也可以
>>> from functools import partial
>>> for a in range(3) :
    def f(a, n):
        return n + a
    f = partial(f, a)
    res.append(f)

>>> res[0](0)
0
>>> res[1](2)
3

答案 4 :(得分:0)

这为您提供了只有一个参数的简洁函数:

res = []
for a in range(3) :
    res.append((lambda a: lambda n: n+a)(a))

这样做可能会更好以便于阅读和提高效率:

def adder(amount):
    return lambda n: n + amount

res = []
for a in range(3) :
    res.append(adder(a))

此外,您的“评估是在for语句的退出处执行的”是错误的。在循环后尝试打印,然后再增加a,然后再次打印。您会看到函数现在使用a的进一步增加的值(仅适用于您的版本,当然,不是我的版本)。这是因为您的函数没有自己的a但使用相同的全局a,并在调用时对其进行评估。