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

Repeatedly reasigning variable pointing to 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))

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

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

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

结果是三层嵌套的每一层都将原始序列的长度乘以3,使得最终的iterable的长度为1×3×3×3 = 27,而不是1×1× 2×3 = 6。所以这根本不是 xs 被重新分配,而是 n 被重新分配。

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

import more_itertools as mo

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

def make_flatten(xs, n):
    # here n is not reassigned in the local scope
    return mo.flatten(rep(x, n) for x in xs)

xs = [0]
for n in [1, 2, 3]:
    xs = make_flatten(xs, n)

print(mo.ilen(xs)) # prints 6, as expected