我在理解下面代码片段的结果时遇到了一些麻烦,并认为这是因为我对功能绑定感到困惑。为什么以下片段会产生不同的结果?
import itertools
def make_funcs(lst):
for val in lst:
def f():
return sum(1 for i in range(10) if i > val)
f.func_name = ">" + str(val)
yield f
## examples:
for f in make_funcs(range(2)):
print(f.func_name, f())
## prints:
>0 9
>1 8
## works as expected:
for f in make_funcs(range(2)):
for g in make_funcs(range(2)):
print(f.func_name, g.func_name, f() + g())
## prints:
>0 >0 18
>0 >1 17
>1 >0 17
>1 >1 16
另一方面,:
## provides results that are counter-intuitive (to me, at least)
for f, g in itertools.product(make_funcs(range(2)), make_funcs(range(2))):
print(f.func_name, g.func_name, f() + g())
## prints:
>0 >0 16
>0 >1 16
>1 >0 16
>1 >1 16
在我看来,它只是抓取/使用/ binding
每个隐式for
循环中的最后一个变量进行计算,即使它正在为函数名称获取正确的变量。
关于范围或函数定义或闭包(或其他)导致这些结果的我缺少什么?
注意:如果我在这个问题上提出的任何标签都无关紧要,请随意删除它们 - 我把它们全部放在一起因为我不确定是什么问题。
答案 0 :(得分:2)
另一个答案解释了为什么你看到你所看到的 - 它是因为函数通过引用捕获外部变量,并且几个函数捕获了相同的val
变量,这些函数看到了对变量
要添加,如果要避免这种情况,如果要按值捕获,一种方法是在内部函数中使用参数和默认参数:
def make_funcs(lst):
for val in lst:
def f(val=val):
return sum(1 for i in range(10) if i > val)
f.func_name = ">" + str(val)
yield f
答案 1 :(得分:1)
所有函数仍引用变量val
。
def make_funcs(lst):
a = []
for val in lst:
def f():
return sum(1 for i in range(10) if i > val)
f.func_name = ">" + str(val)
a.append(f)
return a
所有的结果打印出"反直觉"。
def make_funcs(lst):
a = []
for val in lst:
def f():
return sum(1 for i in range(10) if i > val)
f.func_name = ">" + str(val)
a.append(f)
val = 10
return a
始终会产生0&#39。
但是,因为你使用了一个生成器,val
的值只有在使用之后才会改变,所以一切看起来都很好。使用itertools.product时,docs表示它会这样做:
def product(*args, repeat=1):
# product('ABCD', 'xy') --> Ax Ay Bx By Cx Cy Dx Dy
# product(range(2), repeat=3) --> 000 001 010 011 100 101 110 111
pools = [tuple(pool) for pool in args] * repeat
result = [[]]
for pool in pools:
result = [x+[y] for x in result for y in pool]
for prod in result:
yield tuple(prod)
这意味着它首先迭代两个生成器(有效地将val
的值改变四次),然后才计算结果。
这一切都发生是因为val
在make_funcs
的范围内定义(而不在f
的范围内),所以如果第二次调用生成器会更改{的值{1}},所有函数都引用 new 值。
修改:请同时阅读@newacct
的答案