我正在試驗2個函數,它們模擬Python 2.x和3.x中內置的zip
。第一個返回一個列表(如在Python 2.x的),第二個是發電機功能,它返回一條其結果集在一個時間(如在Python 3.X):使用生成器表達式會導致Python掛起
def myzip_2x(*seqs):
its = [iter(seq) for seq in seqs]
res = []
while True:
try:
res.append(tuple([next(it) for it in its])) # Or use generator expression?
# res.append(tuple(next(it) for it in its))
except StopIteration:
break
return res
def myzip_3x(*seqs):
its = [iter(seq) for seq in seqs]
while True:
try:
yield tuple([next(it) for it in its]) # Or use generator expression?
# yield tuple(next(it) for it in its)
except StopIteration:
return
print(myzip_2x('abc', 'xyz123'))
print(list(myzip_3x([1, 2, 3, 4, 5], [7, 8, 9])))
這運作良好,並給出了預期的輸出zip
內置:
[('a', 'x'), ('b', 'y'), ('c', 'z')]
[(1, 7), (2, 8), (3, 9)]
然後我想到有關更換tuple()
調用其(幾乎)相當於發電機表達式中的列表解析,刪去方括號[]
(爲什麼當生成器應該適用於可迭代的exp時,使用理解創建臨時列表受tuple()
的影響吧?)
但是,這會導致Python掛起。如果執行沒有終止,使用CtrlC(在Windows上的IDLE中),它最終會在幾分鐘後停止,並且(預期的)MemoryError
異常。
調試代碼(例如使用PyScripter)顯示當使用生成器表達式時,從不會引發StopIteration
異常。第一個示例呼叫上面myzip_2x()
不斷增加空元組res
,而第二實例包調用myzip_3x()
產生元組(1, 7)
,(2, 8)
,(3, 9)
,(4,)
,(5,)
,()
,()
,()
,...
。
我錯過了什麼嗎?
而且最後要注意:如果its
成爲每個功能(當列表內涵在tuple()
呼叫使用)的第一行(使用its = (iter(seq) for seq in seqs)
)發電機出現相同的懸掛行爲。
編輯:
謝謝@Blckknght的解釋,你是對的。 This message使用上面的生成器函數的類似示例給出了更多細節。總之,像這樣的生成器表達式只適用於Python 3.5+,它需要文件頂部的from __future__ import generator_stop
語句,並在上面更改StopIteration
和RuntimeError
(同樣,使用生成器表達式而不是列表解析時)。
編輯2:
如對上述最後請注意:如果its
變成一臺發電機(使用its = (iter(seq) for seq in seqs)
)將支持只是一個迭代 - 因爲發電機是一次性的迭代器。因此它在第一次運行while循環時耗盡,並且在隨後的循環中僅獲得空元組。
我可以用任何環路或功能之外運行的代碼確認你的猜測。 –
謝謝你提供這樣一個好的概念性解釋。 – John