142

先史時代 (Python 1.4) では、次のことを行いました。

fp = open('filename.txt')
while 1:
    line = fp.readline()
    if not line:
        break
    print line

Python 2.1 以降では、次のようにしました。

for line in open('filename.txt').xreadlines():
    print line

Python 2.3 で便利な反復子プロトコルを取得する前に、次のことが可能になりました。

for line in open('filename.txt'):
    print line

より詳細な使用例をいくつか見てきました。

with open('filename.txt') as fp:
    for line in fp:
        print line

これは今後推奨される方法ですか?

[編集] with ステートメントがファイルを確実に閉じることはわかりましたが、なぜそれがファイル オブジェクトのイテレータ プロトコルに含まれていないのでしょうか?

4

4 に答える 4

238

以下が好まれる理由は、まさに 1 つです。

with open('filename.txt') as fp:
    for line in fp:
        print line

私たちは皆、ガベージ コレクションのための CPython の比較的決定論的な参照カウント スキームに甘やかされています。with他の仮想的な Python の実装では、メモリを再利用するために他のスキームを使用する場合、ブロックなしでファイルを「十分に迅速に」閉じるとは限りません。

このような実装では、ガベージ コレクターが孤立したファイル ハンドルに対してファイナライザーを呼び出すよりも速くファイルを開くと、OS から「開いているファイルが多すぎます」というエラーが発生する可能性があります。通常の回避策は、GC をすぐにトリガーすることですが、これは厄介なハックであり、ライブラリ内の関数を含め、エラーが発生する可能性のあるすべての関数で実行する必要があります。なんて悪夢だ。

withまたは、ブロックを使用することもできます。

ボーナス質問

(質問の客観的な側面にのみ興味がある場合は、ここで読むのをやめてください。)

ファイルオブジェクトの反復子プロトコルにそれが含まれていないのはなぜですか?

これは API 設計に関する主観的な質問なので、2 つの部分に分けて主観的な回答をします。

直観的には、これは間違っているように感じます。なぜなら、イテレータ プロトコルが 2 つの別々のこと (複数行の反復処理ファイル ハンドルのクローズ) を実行するためです。単純に見える関数に 2 つのアクションを実行させるのはよくない考えです。この場合、イテレータがファイルの内容に準機能的で値ベースの方法で関連付けられているため、特に気分が悪くなりますが、ファイル ハンドルの管理は完全に別のタスクです。目に見えない形で両方を 1 つのアクションに押しつぶすことは、コードを読む人間にとって驚くべきことであり、プログラムの動作について推論することをより困難にします。

他の言語も基本的に同じ結論に達しています。Haskell は、ファイルを繰り返し処理し、ストリームの最後に到達したときにファイルを自動的に閉じることができる、いわゆる「遅延 IO」を簡単にいじりましたが、最近の Haskell で遅延 IO を使用することはほとんど一般的に推奨されていません。ユーザーはwith、Pythonのブロックのように動作する Conduit のような、より明示的なリソース管理にほとんど移行しています。

技術的なレベルでは、反復によってファイル ハンドルが閉じられた場合、Python のファイル ハンドルでやりたいことがいくつかあります。たとえば、ファイルを 2 回繰り返す必要があるとします。

with open('filename.txt') as fp:
    for line in fp:
        ...
    fp.seek(0)
    for line in fp:
        ...

これはあまり一般的な使用例ではありませんが、最初は上の 3 行があった既存のコード ベースに、下の 3 行のコードを追加しただけかもしれないという事実を考慮してください。反復によってファイルが閉じられた場合、それはできません。そのため、反復処理とリソース管理を分離することで、コードのチャンクをより大規模で機能する Python プログラムに構成することが容易になります。

コンポーザビリティは、言語または API の最も重要なユーザビリティ機能の 1 つです。

于 2012-07-19T07:01:48.563 に答える
22

はい、

with open('filename.txt') as fp:
    for line in fp:
        print line

行く方法です。

これ以上冗長ではありません。より安全です。

于 2012-07-19T07:01:35.600 に答える
5

余分な行でオフになっている場合は、次のようなラッパー関数を使用できます。

def with_iter(iterable):
    with iterable as iter:
        for item in iter:
            yield item

for line in with_iter(open('...')):
    ...

Python 3.3 では、yield fromステートメントはこれをさらに短くします。

def with_iter(iterable):
    with iterable as iter:
        yield from iter
于 2012-07-19T07:45:08.967 に答える
-1
f = open('test.txt','r')
for line in f.xreadlines():
    print line
f.close()
于 2016-02-03T02:57:38.913 に答える