14

多くのファイルを 1 つのファイルのように扱いたい。[filenames] => [file objects] => [lines] をジェネレーターで使用する/ファイル全体をメモリに読み込まないための適切なpythonicの方法は何ですか?

私たちは皆、ファイルを開く正しい方法を知っています:

with open("auth.log", "rb") as f:
    print sum(f.readlines())

そして、複数のイテレータ/ジェネレータを 1 つの長いイテレータ/ジェネレータにリンクする正しい方法を知っています。

>>> list(itertools.chain(range(3), range(3)))
[0, 1, 2, 0, 1, 2]

しかし、どうすれば複数のファイルを一緒にリンクし、コンテキスト マネージャーを保持することができるでしょうか?

with open("auth.log", "rb") as f0:
    with open("auth.log.1", "rb") as f1:
        for line in itertools.chain(f0, f1):
            do_stuff_with(line)

    # f1 is now closed
# f0 is now closed
# gross

コンテキスト マネージャーを無視して、次のようなことを行うこともできますが、正しくないと感じます。

files = itertools.chain(*(open(f, "rb") for f in file_names))
for line in files:
    do_stuff_with(line)

それとも、この種のAsync IO - PEP 3156の目的であり、後でエレガントな構文を待つ必要がありますか?

4

1 に答える 1

21

いつもありfileinputます。

for line in fileinput.input(filenames):
    ...

ただし、ソースを読むfileinput.FileInputと、コンテキスト マネージャーとして使用できないようです1contextlib.closingこれを修正するには、FileInputインスタンスには適切に実装されたcloseメソッドがあるため、次を使用できます。

from contextlib import closing
with closing(fileinput.input(filenames)) as line_iter:
    for line in line_iter:
        ...

コンテキスト マネージャーを使用する別の方法として、ファイルをループして行を生成する単純な関数を作成する方法があります。

def fileinput(files):
    for f in files:
        with open(f,'r') as fin:
            for line in fin:
                yield line

ここでの私見は本当に必要ありませんitertools.chain...ここでの魔法はyield、通常の関数を幻想的に怠惰なジェネレーターに変換するために使用されるステートメントにあります。


1余談ですが、python3.2 以降でfileinput.FileInput 、以前に で行ったのとまったく同じことを行うコンテキスト マネージャーとして実装されていますcontextlib。例は次のようになります。

# Python 3.2+ version
with fileinput.input(filenames) as line_iter:
    for line in line_iter:
        ...

ただし、他の例は python3.2+ でも機能します。

于 2013-04-19T01:49:37.880 に答える