一种解决方法是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
raise StopIteration
。
迭代器协议有两部分:容器元素上的“外部”部分返回迭代器,以及“内部”部分,即迭代器本身。
容器必须实现__iter__()
,它返回一个新对象,即迭代器。TarFile.__iter__()
返回一个新TarIter
对象。
迭代器本身 ( TarIter
) 实现__iter__()
(总是返回self
)和next()
. 它还必须对原始容器中的项目有自己的独立索引。这允许您在同一个容器上生成多个不同的迭代器,而不会使单独的迭代相互混淆。
TarFile.next()
但是,它的迭代没有使用单独的索引,所以如果其他人使用由他们提供的伪迭代协议,TarFile
他们会搞乱迭代。
这似乎就是这里发生的事情。在当前使用TarFile.extractfile(filename)
中查找匹配文件,而不是您正在使用的文件。这会破坏“下一项”索引,导致在第一次调用后返回。TarFile
TarFile.next()
TarFile.__iter__()
archive.next()
None
extractfile()
但是,如果您使用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
这更清楚,我敢打赌它也会快一点。