以下两个Python 3代码片段的不同之处在于我认为没有任何区别的更改。但是,当在Mac OS X下的Python 3.5中从IDLE运行时,它们会产生不同的结果。我在运行每个代码段之前重新启动了Python shell。
摘录1:
## The following prints [5, 7] as expected
a = (k for k in range(2, 10)) # generator for [2, 3, 4, 5, 6, 7, 8, 9]
p = next(a) # p is 2
a = (k for k in a if k % p != 0) # remove multiples of 2: generator for [3, 5, 7, 9]
q = next(a) # q is 3
a = (k for k in a if k % q != 0) # remove multiples of 3: generator for [5, 7]
print(list(a)) # prints [5, 7]
摘录2:[唯一的区别是在第四行和第五行代码中使用 p 而不是 q 。]
## The following prints [4, 5, 7, 8]
a = (k for k in range(2, 10))
p = next(a)
a = (k for k in a if k % p != 0)
p = next(a) # Using p instead of q
a = (k for k in a if k % p != 0) # Ditto
print(list(a)) # prints [4, 5, 7, 8]
因此,重用对象名称 p 似乎具有深远的影响。
我错过了什么?我没有成功地试图让这个例子变得更小。
答案 0 :(得分:0)
问题在于以下两行:
a = (k for k in a if k % p != 0)
p = next(a) # Using p instead of q
“新a
”会产生符合条件a
的“旧k % p != 0
”元素。但是,该条件仅在生成器a
请求新元素时进行评估,然后使用值p
。在你的情况下,那将是 3 (并且不再 2,正如人们所期望的那样)。
更长的解释:
让我们跟踪代码中发生的事情。我使用了三个不同的a
变量(a
,a2
和a3
)来避免混淆。
a = (k for k in range(2, 10))
创建了一个生成器a
,此时list(a)
将导致[2, 3, 4, 5, 6, 7, 8, 9]
。
p = next(a)
第一个值来自a
,并已分配给变量p
。 p
变为2
,list(a)
此时会产生[3,4,5,6,7,8,9]。
a2 = (k for k in a if k % p != 0)
创建一个新的生成器。它会生成a
所产生的与条件k % p != 0
匹配的所有值。由于p
当前等于2
,list(a2)
会导致[3, 5, 7, 9]
(BTW,生成器a
也会在此过程中耗尽。)
p = next(a2)
从生成器a2
请求新值。后者从a
请求值,获取3
,并检查它是否与上述条件k % p != 0
匹配。由于p
目前等于2
,因此确实如此,而a2
会产生3
,这将成为p
的新值。
由于a
消耗了一个值,list(a)
此时会产生[4, 5, 6, 7, 8, 9]
。但是,list(a2)
现在会产生[4, 5, 7, 8]
!这是因为k % p != 0
将使用p
的当前值3
,使a2
跳过值6
和9
。< / p>
a3 = (k for k in a2 if k % p != 0)
新生成器a3
与a2
基本相同,因为它们都具有相同的条件。
print(list(a3))
生成器a3
已耗尽,从而耗尽a2
,这会耗尽a
的值,从而导致观察到的结果为[4, 5, 7, 8]
。
此处,a
已产生[4, 5, 6, 7, 8, 9]
,但3
的倍数已被a2
过滤掉,a3
未过滤任何其他内容(因为它是与a2
)基本相同。