Чи читає весь файл, залишає ручку файлу відкритою?


372

Якщо ви читаєте цілий файл із, content = open('Path/to/file', 'r').read()чи залишається відкрита ручка файлу до тих пір, поки скрипт не вийде? Чи є більш стислий спосіб прочитати цілий файл?

Відповіді:


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 різних реалізацій сміття !


24
Деякий час реалізацією Python насправді не було; але покладатися на деталі реалізації насправді не є пітонічним.
Карл Кнечтел

Це все ще залежить від впровадження, або це вже було стандартизовано? Не викликати __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 .

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

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.