Оставляет ли чтение всего файла открытый дескриптор файла?

372

Если вы читаете весь файл с content = open('Path/to/file', 'r').read()помощью, останется ли дескриптор файла открытым до завершения работы сценария? Есть ли более краткий способ прочитать весь файл?

TMC
источник

Ответы:

585

Ответ на этот вопрос зависит от конкретной реализации Python.

Чтобы понять, о чем это все, обратите особое внимание на реальный fileобъект. В вашем коде этот объект упоминается только один раз в выражении и становится недоступным сразу после 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 разных реализаций сборки мусора !

SingleNegationElimination
источник
24
Некоторое время на самом деле не было других реализаций Python; но полагаться на детали реализации на самом деле не Pythonic.
Карл Кнехтель,
Это все еще зависит от реализации или уже стандартизировано? Не звонить __exit__()в таких случаях звучит как недостаток дизайна.
rr-
2
@jgmjgm Именно из-за этих 3-х проблем, из-за непредсказуемости GC, try/ из-за finallyтого , что он неуклюжий и чрезвычайно распространенный бесполезный из обработчиков очистки, который withрешается. Разница между «явным закрытием» и «управлением с with» заключается в том, что обработчик выхода вызывается, даже если выдается исключение. Вы могли бы поместить close()в finallyпредложение, но это не сильно отличается от использования withвместо этого, немного более грязный (3 дополнительные строки вместо 1), и немного сложнее сделать правильно.
SingleNegationElimination
1
То, что я не понимаю, это то, почему «с» будет более надежным, поскольку это также не явно. Это потому, что спецификация говорит, что она должна делать это всегда так?
jgmjgm
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 .

Более того, таким образом все содержимое файла может храниться в переменной, которая может быть желательна в некоторых случаях, вместо того, чтобы циклически перемещаться по файлу, как указывалось в предыдущем ответе .

Андреас Л.
источник