[英]Inconsistent behavior of python generators
下面的python代碼產生[(0,0),(0,7)...(0,693)]而不是預期的元組列表,它組合了3的倍數和7的倍數:
multiples_of_3 = (i*3 for i in range(100))
multiples_of_7 = (i*7 for i in range(100))
list((i,j) for i in multiples_of_3 for j in multiples_of_7)
此代碼修復了此問題:
list((i,j) for i in (i*3 for i in range(100)) for j in (i*7 for i in range(100)))
問題:
生成器對象是迭代器,因此是一次性的。 這不是能產生任何數量的獨立迭代的迭代 。 這種行為不是你可以用某個開關改變的東西,所以任何解決方法相當於使用可迭代(例如列表)而不是生成器或重復構建生成器。
第二個片段是后者。 根據定義,它等同於循環
for i in (i*3 for i in range(100)):
for j in (i*7 for i in range(100)):
...
希望在這里,在外循環的每次迭代中重新評估后一個生成器表達式並不奇怪。
正如您所發現的,生成器表達式創建的對象是一個迭代器(更准確地說是一個生成器迭代器 ),設計為只消耗一次。 如果你需要一個可重置的生成器,只需創建一個真正的生成器並在循環中使用它:
def multiples_of_3(): # generator
for i in range(100):
yield i * 3
def multiples_of_7(): # generator
for i in range(100):
yield i * 7
list((i,j) for i in multiples_of_3() for j in multiples_of_7())
你的第二個代碼是有效的,因為內循環的表達式列表( (i*7 ...)
)是在外循環的每次傳遞上計算的。 這導致每次都創建一個新的生成器迭代器,它為您提供所需的行為,但代價是代碼清晰度。
要了解發生了什么,請記住,當for
循環迭代時,迭代器沒有“重置”。 (這是一個特性;這樣的重置會破壞迭代的大型迭代器,對於生成器來說是不可能的。)例如:
multiples_of_2 = iter(xrange(0, 100, 2)) # iterator
for i in multiples_of_2:
print i
# prints nothing because the iterator is spent
for i in multiples_of_2:
print i
......而不是這個:
multiples_of_2 = xrange(0, 100, 2) # iterable sequence, converted to iterator
for i in multiples_of_2:
print i
# prints again because a new iterator gets created
for i in multiples_of_2:
print i
生成器表達式等同於被調用的生成器,因此只能迭代一次。
如果要將生成器表達式轉換為多遍迭代,則可以以相當常規的方式完成。 例如:
class MultiPass(object):
def __init__(self, initfunc):
self.initfunc = initfunc
def __iter__(self):
return self.initfunc()
multiples_of_3 = MultiPass(lambda: (i*3 for i in range(20)))
multiples_of_7 = MultiPass(lambda: (i*7 for i in range(20)))
print list((i,j) for i in multiples_of_3 for j in multiples_of_7)
從定義事物的角度來看,輸入的工作量相似:
def multiples_of_3():
return (i*3 for i in range(20))
但是從用戶的角度來看,他們編寫multiples_of_3
而不是multiples_of_3()
,這意味着對象multiples_of_3
與任何其他可迭代對象是多態的,例如tuple
或list
。
鍵入lambda:
的需要lambda:
有點不優雅,真實。 我不認為在語言中引入“可迭代的理解”會給你提供你想要的東西,同時保持向后兼容性。 但是只有那么多標點字符,我懷疑這會被認為是值得的。
我發現的真正問題是關於單通道和多通道迭代,以及目前沒有標准機制來確定可迭代的單通道還是多通道的事實:請參閱單通道和多通道迭代
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.