0

我在理解以下代码段的结果时遇到了一些麻烦,我认为这是因为我对函数绑定感到困惑。为什么以下片段会产生不同的结果?

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进行计算,即使它为函数名称抓取了正确的变量。

关于导致这些结果的范围或函数定义或闭包(或其他),我缺少什么?

注意:如果我在这个问题上放置的任何标签是无关紧要的,请随时删除它们 - 我将它们全部放置是因为我不确定问题是什么。

4

2 回答 2

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
于 2015-08-05T07:47:55.333 回答
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。

但是,因为您使用了生成器,所以val只有在使用后才会更改值,所以一切看起来都很好。使用 itertools.product 时,文档说它这样做:

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),所以如果第二次调用生成器更改了 的值val,所有函数都会引用值。

编辑:还请阅读@newacct 的答案

于 2015-08-03T06:00:02.543 回答