解決方法是直接使用extractfile
與tarinfo而不是名稱。這工作:
def tariter(filename):
with tarfile.open(filename) as archive:
while True:
tarinfo = archive.next()
if tarinfo is None:
break
if tarinfo.isreg():
handle = archive.extractfile(tarinfo) # LINE CHANGED
data = handle.read()
handle.close()
yield tarinfo, data
至於爲什麼這種情況正在發生:TarFile.next()
不不實現迭代器協議,因爲它返回None
,而不是提高StopIteration
。
有兩個部分,以迭代器協議:該返回一個迭代容器元件上的「外」部和「內」部是迭代器本身。
容器必須實現__iter__()
,它返回一個新的對象,它是迭代器。 TarFile.__iter__()
返回一個新的TarIter
對象。
迭代器本身(TarIter
)實現了__iter__()
(總是返回self
)和next()
。它還必須具有自己的獨立索引來指向原始容器中的項目。這可以讓你在同一個容器上生成幾個不同的迭代器,而不會讓單獨的迭代互相混淆。
TarFile.next()
,但是,確實不使用單獨的索引的迭代,所以如果其他人使用由TarFile
他們會弄亂迭代所提供的僞迭代協議。
這似乎是這裏發生了什麼。 TarFile.extractfile(filename)
查找使用的TarFile.next()
代替TarFile.__iter__()
這是你用什麼在當前TarFile
匹配的文件。這會破壞「下一項」索引,導致archive.next()
在第一次調用extractfile()
後返回None
。
但是,如果你使用extractfile(tarinfo)
,該tarinfo
對象中有足夠的元數據TarFile
提取字符串內容,而不通過archive
對象尋找匹配文件名查找。因此,archive.extractfile(tarinfo)
可能比archive.extractfile(tarinfo.name)
更快。
一般來說,集合對象(如TarFile
)應該而不是自己迭代,但產生一個新的對象來遍歷它們。 TarFile.next()
僅僅存在一個壞設計的氣味。也許有一個很好的理由,但你不必使用它!
而是執行此操作:
def tariter(filename):
with tarfile.open(filename) as archive:
# use TarIter object for iteration over archive
for tarinfo in archive:
if tarinfo.isreg():
handle = archive.extractfile(tarinfo)
data = handle.read()
handle.close()
yield tarinfo, data
這是更清晰,我敢打賭,這是一個有點快了。
你確定'archive.next'返回None嗎?這也可能在'tarinfo.isreg()'上失敗,如果這是錯誤的,它可能會輸入一個spinloop,直到調用'break'。 – slezica 2013-02-16 18:17:20
是的,我添加了一個'print'語句來調試它。另外,它被記錄爲'archive.next'應該返回'None',但是隻有當它到達歸檔的末尾時......也是,'isreg()'只是爲了過濾目錄,因爲讀取它們沒有意義內容,據我所知這應該無關緊要。 – liori 2013-02-16 18:20:19
我剛剛在本地嘗試了這段代碼,並得到了與您相同的結果。這是一個合同問題,你引發了我的好奇心:)我會看看我能找到什麼 – slezica 2013-02-16 18:22:45