【问题标题】:Why does redefining a variable used in a generator give strange results? [duplicate]为什么重新定义生成器中使用的变量会产生奇怪的结果? [复制]
【发布时间】:2021-12-08 02:41:40
【问题描述】:

我的一个朋友问我这段代码:

array = [1, 8, 15]
gen = (x for x in array if array.count(x) > 0)
array = [2, 8, 22]
print(list(gen))

输出:

[8]

其他元素去哪了?

【问题讨论】:

  • 理论:生成器表达式被计算到for x in array 但在创建时不会进一步,然后 array 被重新定义, 然后在新数组上评估count 检查。
  • 非常有趣的问题。我很惊讶重新定义array 会对gen 产生影响。可能会导致非常难以发现的错误。
  • @tobias_k 这显然是正确的,正如第二个阵列上的实验所显示的那样
  • 虽然这是重复的,但我相信这是一个非常有价值的路标,基于显着不同的标题(出于 SEO 目的)以及此处提供的解释与重复目标的差异。它在不到一周的时间内就获得了如此高的分数,这也说明了这一点(比欺骗目标在 3 年后获得的分数更高)。

标签: python generator


【解决方案1】:

答案在生成器表达式的 PEP 中,尤其是会话 Early Binding vs Late biding:

经过多次讨论,决定第一个(最外层) for-expression 应立即计算,其余的 执行生成器时计算表达式。

所以基本上array 在:

x for x in array 

使用原始列表[1, 8, 15](即立即)进行评估,而另一个:

if array.count(x) > 0

在生成器执行时评估:

print(list(gen))

此时array 引用了一个新列表[2, 8, 22]

【讨论】:

  • 谢谢,还有 1 个问题,所以 list(gen),因为计数 > 0,应该是 od [2, 8, 22] 的所有元素吧?但它只有 8 个。
  • @picklerick 不,因为 for-loop 表达式是用数组的第一个值计算的,基本上你的代码相当于list(x for x in [1, 8, 15] if [2, 8, 22].count(x) > 0)
  • 当 PEP 说“第一个(最外层的)for 表达式应该立即被计算”时,指的是array,而不是x for x in array。这是for 迭代的表达式。尝试立即评估 x for x in 没有任何意义。
  • 在生成器表达式中添加一些打印调用说明了正在发生的事情。 Live demo
  • (x for x in iterable if <condition>) 可以重写为tmp = iterable; (x for x in tmp if <condition>),其中<condition> 与以前完全相同(因此无法访问tmp)。跨度>
【解决方案2】:

如果你给每个数组一个唯一的名字而不是重新绑定array,这就更清楚了:

array1 = [1, 8, 15]
gen = (x for x in array1 if array2.count(x) > 0)
array2 = [2, 8, 22]
print(list(gen))

x for x in array1 在创建生成器时被评估,但 if array2.count(x) > 0 被延迟评估,这就是为什么你已经可以引用一个尚未定义的变量

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2020-07-11
    • 2013-03-15
    • 1970-01-01
    • 2021-02-20
    • 2015-10-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多