【问题标题】:Repeatedly reasigning variable pointing to iterable反复重新分配指向可迭代的变量
【发布时间】:2021-11-18 02:24:33
【问题描述】:

考虑以下代码:

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

【问题讨论】:

  • kaya3 是正确的,如果您想进一步证明,您可以在 print(mo.ilen(xs)) 之前插入将 n 分配给其他内容的行,您将看到结果相应更改(导致 n**3 )。

标签: python iterable


【解决方案1】:

您的mo.flatten(...) 调用是在生成器表达式上进行的,这些表达式是惰性求值的,因此仅当mo.ilen(xs) 必须在最后一行使用生成器时才会进行求值。此时,变量n 的值为3,因此这是n 的值,用于评估close 超过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

【讨论】:

  • 太棒了,谢谢!以lambda 结束也可以,所以我可能会这样做:xs = (lambda n: mo.flatten(rep(x, n) for x in xs))(n)
猜你喜欢
  • 2014-03-13
  • 2021-11-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-03-19
  • 1970-01-01
  • 2019-04-06
相关资源
最近更新 更多