反复重新分配指向可迭代的变量

psa*_*rka 1 python iterable

考虑以下代码:

import more_itertools as mo

def rep(x, n):
    for i in range(n):
        yield x

xs = [0]
for n in [1, 2, 3]:
    xs = mo.flatten(rep(x, n) for x in xs)

print(mo.ilen(xs))
Run Code Online (Sandbox Code Playgroud)

正确答案应该是 6,但它打印出 27,为什么?

请注意,它more_itertools.flatten做了显而易见的事情,实际上是itertools.chain.from_iterable. more_itertools.ilen也做了显而易见的事情,只计算元素。我不认为所涉及的功能有任何错误,只是重新签名的问题xs

kay*_*ya3 5

您的mo.flatten(...)调用是在延迟评估的生成器表达式上进行的,因此仅当必须在最后一行使用生成器时才会进行评估mo.ilen(xs)。此时,变量的n值为 3,因此这是在计算结束于 的n生成器表达式时使用的值。(请注意,虽然您可能认为只存在于循环内,但它仍然在循环之后的范围内,因为 Python 没有块作用域。)nn

\n

结果是,三层嵌套中的每一层都将原始序列的长度乘以3,因此最终的iterable的长度为1\xc3\x973\xc3\x973\xc3\x973\xc2\xa0=\xc2 \xa027 而不是 1\xc3\x971\xc3\x972\xc3\x973\xc2\xa0=\xc2\xa06。所以这根本不是关于xs重新分配,而是关于n被重新分配。

\n

要获得预期的行为(无需急切求值),您可以将生成器表达式包装在函数调用中,以便关闭n每个生成器表达式中的不同内容:

\n
import more_itertools as mo\n\ndef rep(x, n):\n    for i in range(n):\n        yield x\n\ndef make_flatten(xs, n):\n    # here n is not reassigned in the local scope\n    return mo.flatten(rep(x, n) for x in xs)\n\nxs = [0]\nfor n in [1, 2, 3]:\n    xs = make_flatten(xs, n)\n\nprint(mo.ilen(xs)) # prints 6, as expected\n
Run Code Online (Sandbox Code Playgroud)\n