1. ホーム
  2. python

[解決済み] Pythonでファイルを一行ずつ読むにはどうしたらいいですか?

2022-05-16 23:58:13

質問

前時代(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ステートメントがファイルのクローズを保証することはわかりますが、なぜファイルオブジェクトのためのイテレータプロトコルにそれが含まれていないのでしょうか?

どのように解決するのですか?

以下のようにすると良いという理由は、まさに一つです。

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

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

このような実装では、ガベージコレクタが孤立したファイルハンドルのファイナライザを呼び出すよりも速く、コードがファイルを開くと、OS から "too many files open" エラーを受け取ることがあります。 通常の回避策は、GC を直ちに起動することですが、これは厄介なハックで ごとに 関数で実行しなければなりません。 なんという悪夢でしょう。

あるいは、単に with ブロックを使うこともできます。

ボーナス質問

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

なぜそれがファイルオブジェクトのイテレータプロトコルに含まれていないのでしょうか?

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

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

他の言語も基本的には同じ結論に達しています。 Haskell は一時期、いわゆる "lazy IO" と呼ばれる、ファイルを繰り返し処理し、ストリームの最後に到達すると自動的にクローズさせることができるものに浮気しましたが、最近では Haskell で lazy IO を使うことはほぼ一般的に推奨されておらず、Haskell ユーザーはほとんど Conduit などのより明確なリソース管理へと移行しています。 with ブロックのように動作します。

技術的なレベルでは、Pythonでファイルハンドルを使って行いたいことがいくつかありますが、もし反復処理がファイルハンドルを閉じてしまうと、うまく動作しないでしょう。 たとえば、私がファイルに対して2回反復処理を行う必要があるとします。

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

これはあまり一般的ではない使用例ですが、もともと上の3行があった既存のコードベースに、下の3行のコードを追加しただけかもしれないという事実を考えてみてください。 もしイテレーションでファイルが閉じられてしまったら、そんなことはできません。 ですから、反復処理とリソース管理を別々にすることで、コードの塊をより大きな、動作するPythonプログラムに構成することがより簡単になるのです。

コンポーザビリティは言語やAPIの最も重要なユーザビリティの特徴の1つです。