ファイル全体を読み取っても、ファイルハンドルは開いたままですか?


372

ファイル全体を読み取る場合content = open('Path/to/file', 'r').read()、スクリプトが終了するまでファイルハンドルは開いたままですか?ファイル全体を読み取るより簡潔な方法はありますか?

回答:


585

その質問への答えは、特定のPython実装によって多少異なります。

これが何であるかを理解するには、実際のfileオブジェクトに特に注意してください。コードでは、そのオブジェクトは式で1回だけ言及され、read()呼び出しが戻った直後にアクセスできなくなります。

これは、ファイルオブジェクトがガベージであることを意味します。残っている唯一の質問は、「ガベージコレクターがファイルオブジェクトを収集するのはいつですか?」です。

参照カウンターを使用するCPythonでは、この種のゴミはすぐに検出されるため、すぐに収集されます。これは一般的に他のpython実装には当てはまりません。

ファイルを確実に閉じるためのより良い解決策は、次のパターンです。

with open('Path/to/file', 'r') as content_file:
    content = content_file.read()

ブロックが終了した直後に常にファイルを閉じます。例外が発生しても。

編集:それにより細かい点を置くには:

コンテキストマネージャー設定file.__exit__()で「自動的に」呼び出される以外に、自動的に呼び出される(つまり、自分で明示的に呼び出す以外に)with唯一の方法file.close()は、を介する方法file.__del__()です。これは、いつ__del__()呼び出されるのかという疑問につながります。

正しく記述されたプログラムは、ファイナライザがプログラムの終了前の任意の時点で実行されることを想定できません。

-https://devblogs.microsoft.com/oldnewthing/20100809-00/?p = 13203

特に:

オブジェクトが明示的に破棄されることはありません。ただし、それらが到達不能になると、ガベージコレクションされる可能性があります。実装では、ガベージコレクションを延期または完全に省略することができます。到達可能なオブジェクトが収集されない限り、ガベージコレクションの実装方法は実装品質の問題です。

[...]

CPythonは現在、参照カウント方式を使用しています(オプションで)循環リンクされたガベージの遅延検出。これは、ほとんどのオブジェクトが到達不能になるとすぐに収集しますが、循環参照を含むガベージを収集することは保証されていません。

- https://docs.python.org/3.5/reference/datamodel.html#objects-values-and-types

(エンファシス鉱山)

しかし、それが示唆するように、他の実装には他の振る舞いがあるかもしれません。例として、PyPyに6つの異なるガベージコレクションの実装があります。


24
しばらくの間、実際には他のPython実装はありませんでした。しかし、実装の詳細に依存することは、実際にはPythonicではありません。
Karl Knechtel、2011

それはまだ実装固有ですか、それともすでに標準化されていますか?__exit__()そのような場合に呼び出さないことは、設計上の欠陥のように聞こえます。
rr-

2
@jgmjgmこれらの3つの問題が原因です。GCが予測不可能である、try/ finally手ごわい、非常に一般的な役に立たないクリーンアップハンドラーがwith解決します。「明示的に閉じる」と「で管理するwith」の違いは、例外がスローされても出口ハンドラーが呼び出されることです。あなたは置くことができるclose()finally句が、それはあまり使用して異なるではないwith代わりに、ビットメシエ(3つの余分な行の代わりに、1)、そして少し難しくちょうど権利を取得します。
SingleNegationElimination 2018

1
それについて私が理解していないのは、明示的ではないため、「with」がもう信頼できる理由です。それは仕様がそれを常にそのように実装することをしなければならないことを言っているからですか?
jgmjgm 2018

3
@jgmjgmは、、、[...]とwith foo() as f: [...]基本的に同じでf = foo()例外が処理されるため、より信頼性が高く、常に呼び出されます。したがって、ファイルは常に閉じられます。f.__enter__()f.__exit__() __exit__
neingeist

104

pathlibを使用できます。

Python 3.5以降の場合:

from pathlib import Path
contents = Path(file_path).read_text()

古いバージョンのPythonでは、pathlib2を使用します

$ pip install pathlib2

次に:

from pathlib2 import Path
contents = Path(file_path).read_text()

これは実際のread_text 実装です:

def read_text(self, encoding=None, errors=None):
    """
    Open the file in text mode, read it, and close the file.
    """
    with self.open(mode='r', encoding=encoding, errors=errors) as f:
        return f.read()

2

さて、各行を処理するために行ごとにファイルを読み取る必要がある場合は、

with open('Path/to/file', 'r') as f:
    s = f.readline()
    while s:
        # do whatever you want to
        s = f.readline()

またはさらに良い方法:

with open('Path/to/file') as f:
    for line in f:
        # do whatever you want to

0

ファイルのコンテンツを単一の文字列として取得する代わりに、ファイルを構成するすべての行のリストとしてコンテンツ保存すると便利です

with open('Path/to/file', 'r') as content_file:
    content_list = content_file.read().strip().split("\n")

ご覧のとおり、このスレッドのメインの回答に連結されたメソッド.strip().split("\n")を追加する必要があります

ここ.strip()では、ファイル文字列全体の末尾にある空白文字と改行文字を削除.split("\n")し、改行文字 \ nごとにファイル文字列全体を分割して実際のリストを生成します

さらに、この方法では、前の回答で指摘したようにファイルを1行ずつループする代わりに、ファイルコンテンツ全体を変数に格納できます。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.