回避策は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
なぜこれが起こっているのかについては、 raise ではなく返すため、 iterator プロトコルを実装してTarFile.next()いません。NoneStopIteration
イテレータ プロトコルには 2 つの部分があります。イテレータを返すコンテナ要素の「外側」部分と、イテレータ自体である「内側」部分です。
コンテナは、イテレータである新しい__iter__()オブジェクトを返す を実装する必要があります。新しいオブジェクトを返します。TarFile.__iter__()TarIter
イテレータ自体 ( ) は(常に を返す) およびをTarIter実装します。また、元のコンテナー内のアイテムへの独自の独立したインデックスも必要です。これにより、別々の反復が互いに混乱することなく、同じコンテナーに対して複数の異なる反復子を生成できます。__iter__()selfnext()
TarFile.next()、しかし、その反復に別のインデックスを使用しないため、誰かが提供する疑似反復プロトコルを使用するTarFileと、反復が台無しになります。
これがここで起こっていることのようです。あなたが使用していたものではなく、現在の使用TarFile.extractfile(filename)で一致するファイルを探します。これにより、「次のアイテム」インデックスが破損し、最初の呼び出しの後に返されます。TarFileTarFile.next()TarFile.__iter__()archive.next()Noneextractfile()
ただし、 を使用するextractfile(tarinfo)と、オブジェクト内で一致するファイル名を探すことなく、文字列の内容を抽出するのにtarinfo十分なメタデータがオブジェクトに含まれます。したがって、はおそらく よりも高速です。TarFilearchivearchive.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
これはより明確であり、少し高速でもあるに違いありません。