动态函数创建和函数体评估
Dynamic function creation and function body evaluation
考虑以下 Python 3 条指令
res = []
for a in range(3) :
res.append(lambda n : n + a)
目的是构建一个包含三个函数 res[0]
、res[1]
和 res[2]
的列表 res
,使得 res[i](n)
return s n + i
,对于 [0, 1, 2]
.
中的所有 i
尽管如此,还是得到了
>>> res[0](0)
2
而不是
>>> res[0](0)
0
一个也有
>>> res[1](2)
4
此行为的解释是,在示例的任何动态生成的匿名函数的主体中的表达式 n + a
中,符号 a
在函数创建时未被计算。求值在for语句的出口执行,解释了为什么所有函数res[0]
、res[1]
和res[2]
return它们的参数值加上2è (because
abrowses
range(3)and
2` 是它的最后一个值。
请注意,问题不在于我们使用了匿名函数。确实,说明
res = []
for a in range(3) :
def f(n) :
return n + a
res.append(f)
导致相同的行为。
另请注意,我们可以通过使用Python的函数eval
来满足上面列出的objective:
res = []
for a in range(3) :
s = "lambda n : n + %s" %(a)
res.append(eval(s))
诀窍在于res
的每个函数的创建都考虑了a
的动态值。
现在我的问题是
- 这是错误还是功能?
- 是否有其他方法不通过
eval
以获得预期的行为?
我不能和#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)
因为 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
这也行,如果你不想使用 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
这为您提供了只有一个参数的干净函数:
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))
还有,你的"The evaluation is performed at the exit of the for statement"是错误的。尝试在循环后打印,然后进一步增加 a
,然后再次打印。您会看到这些函数现在使用进一步增加的 a
值(当然,仅适用于您的版本,不适用于我的版本)。那是因为您的函数没有自己的 a
,而是使用相同的全局 a
,并在调用它们时对其求值。
考虑以下 Python 3 条指令
res = []
for a in range(3) :
res.append(lambda n : n + a)
目的是构建一个包含三个函数 res[0]
、res[1]
和 res[2]
的列表 res
,使得 res[i](n)
return s n + i
,对于 [0, 1, 2]
.
i
尽管如此,还是得到了
>>> res[0](0)
2
而不是
>>> res[0](0)
0
一个也有
>>> res[1](2)
4
此行为的解释是,在示例的任何动态生成的匿名函数的主体中的表达式 n + a
中,符号 a
在函数创建时未被计算。求值在for语句的出口执行,解释了为什么所有函数res[0]
、res[1]
和res[2]
return它们的参数值加上2è (because
abrowses
range(3)and
2` 是它的最后一个值。
请注意,问题不在于我们使用了匿名函数。确实,说明
res = []
for a in range(3) :
def f(n) :
return n + a
res.append(f)
导致相同的行为。
另请注意,我们可以通过使用Python的函数eval
来满足上面列出的objective:
res = []
for a in range(3) :
s = "lambda n : n + %s" %(a)
res.append(eval(s))
诀窍在于res
的每个函数的创建都考虑了a
的动态值。
现在我的问题是
- 这是错误还是功能?
- 是否有其他方法不通过
eval
以获得预期的行为?
我不能和#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)
因为 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
这也行,如果你不想使用 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
这为您提供了只有一个参数的干净函数:
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))
还有,你的"The evaluation is performed at the exit of the for statement"是错误的。尝试在循环后打印,然后进一步增加 a
,然后再次打印。您会看到这些函数现在使用进一步增加的 a
值(当然,仅适用于您的版本,不适用于我的版本)。那是因为您的函数没有自己的 a
,而是使用相同的全局 a
,并在调用它们时对其求值。