反复重新分配指向可迭代的变量
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
考虑以下代码:
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