6

「猫」のようなPythonジェネレーターを書いています。私の具体的な使用例は、「grep のような」操作です。条件が満たされた場合にジェネレーターから抜け出せるようにしたい:

summary={}
for fn in cat("filelist.dat"):
    for line in cat(fn):
        if line.startswith("FOO"):
            summary[fn] = line
            break

そのため、発生したbreak場合、cat()ジェネレーターがファイルハンドルを終了して閉じる必要がありますfn

合計 30 GB のデータを含む 100k ファイルを読み取る必要があり、FOOキーワードはヘッダー領域で発生するため、この場合、cat()関数がファイルの読み取りをできるだけ早く停止することが重要です。

この問題を解決する方法は他にもありますが、開いているファイル ハンドルを持つジェネレーターを早期に終了する方法を知りたいと思っています。おそらく、Python はすぐにそれらをクリーンアップし、ジェネレーターがガベージ コレクションされるとそれらを閉じますか?

ありがとう、

イアン

4

4 に答える 4

6

ジェネレーターには、ステートメントで発生させるcloseメソッドがあります。この例外を明確にキャッチした場合は、ティアダウン コードを実行できます。GeneratorExityield

import contextlib
with contextlib.closing( cat( fn ) ):
    ...

そして次にcat

try:
    ...
except GeneratorExit:
    # close the file

closeこれを行うためのより簡単な方法が必要な場合 (ジェネレーターで難解な方法を使用せずに)、cat文字列の代わりにファイルのようなオブジェクトを取得して開き、ファイル IO を自分で処理します。

for filename in filenames:
    with open( filename ) as theFile:
        for line in cat( theFile ):
            ...

ただし、ガベージ コレクションがすべてを処理するため、基本的にこれについて心配する必要はありません。まだ、

明示的は暗黙的よりも優れています

于 2010-09-03T16:39:58.817 に答える
5

コンテキスト プロトコルイテレータ プロトコルを同じオブジェクトに実装することで、次のような非常に優れたコードを記述できます。

with cat("/etc/passwd") as lines:
    for line in lines:
        if "mail" in line:
            print line.strip()
            break

これは、Linux ボックスで Python 2.5 を使用してテストされたサンプル実装です。/etc/passwduser の行が見つかるまでの行を読み取り、audio停止します。

from __future__ import with_statement


class cat(object):

    def __init__(self, fname):
        self.fname = fname

    def __enter__(self):
        print "[Opening file %s]" % (self.fname,)
        self.file_obj = open(self.fname, "rt")
        return self

    def __exit__(self, *exc_info):
        print "[Closing file %s]" % (self.fname,)
        self.file_obj.close()

    def __iter__(self):
        return self

    def next(self):
        line = self.file_obj.next().strip()
        print "[Read: %s]" % (line,)
        return line


def main():
    with cat("/etc/passwd") as lines:
        for line in lines:
            if "mail" in line:
                print line.strip()
                break


if __name__ == "__main__":
    import sys
    sys.exit(main())

またはさらに簡単です:

with open("/etc/passwd", "rt") as f:
    for line in f:
        if "mail" in line:
            break

ファイル オブジェクトはイテレータ プロトコルを実装します ( http://docs.python.org/library/stdtypes.html#file-objectsを参照) 。

于 2010-09-03T18:11:17.220 に答える
2

次の例も考慮してください。

def itertest():
    try:
        for i in xrange(1000):
            print i
            yield i
    finally:
        print 'finally'

x = itertest()

for i in x:
    if i > 2:
        break

print 'del x'
del x

print 'exit'

0
1
2
3
del x
finally
exit

イテレータがクリーンアップされた後に finally が実行されることを示しています。__del__(self)を呼び出していると思いますself.close()。こちらも参照してください: https://docs.python.org/2.7/reference/expressions.html#generator.close

于 2015-07-27T13:18:23.753 に答える