Подтвердить что ты не робот

Нужно ли закрывать файлы, которые не имеют к ним ссылок?

Как полный новичок в программировании, я пытаюсь понять основные понятия открытия и закрытия файлов. Одно упражнение, которое я делаю, это создать script, который позволяет мне копировать содержимое из одного файла в другой.

in_file = open(from_file)
indata = in_file.read()

out_file = open(to_file, 'w')
out_file.write(indata)

out_file.close()
in_file.close()

Я попытался сократить этот код и придумал следующее:

indata = open(from_file).read()
open(to_file, 'w').write(indata)

Это работает и выглядит немного более эффективным для меня. Однако это также я смущаюсь. Я думаю, что я оставил ссылки на открытые файлы; не было необходимости в переменных in_file и out_file. Однако это оставляет меня с двумя открытыми файлами, но не имеет к ним никакого отношения? Как их закрыть, или нет необходимости?

Любая помощь, которая проливает некоторый свет на эту тему, очень ценится.

4b9b3361

Ответ 1

Вы спросили об "базовых понятиях", поэтому давайте возьмем их сверху: когда вы открываете файл, ваша программа получает доступ к системному ресурсу, то есть к чему-то вне программы собственное пространство памяти. Это в основном немного волшебства, предоставляемого операционной системой (системный вызов, в терминологии Unix). Скрытый внутри файлового объекта является ссылкой на "файловый дескриптор", фактический ресурс ОС, связанный с открытым файлом. Закрытие файла сообщает системе о выпуске этого ресурса.

В качестве ресурса ОС количество файлов, которые может сохраняться в процессе, ограничено:. До сих пор предел для каждого процесса составлял около 20 в Unix. В настоящее время ящик OS X накладывает ограничение на 256 открытых файлов (хотя это назначенный предел и может быть поднят). Другие системы могут устанавливать пределы несколько тысяч или в десятки тысяч (на каждого пользователя, а не на процесс в этом случае). Когда ваша программа заканчивается, все ресурсы автоматически освобождаются. Поэтому, если ваша программа открывает несколько файлов, что-то делает с ними и выходит, вы можете быть неаккуратными, и вы никогда не узнаете разницу. Но если ваша программа откроет тысячи файлов, вам удастся открыть открытые файлы, чтобы избежать превышения ограничений ОС.

Еще одно преимущество закрытия файлов перед тем, как выйти из вашего процесса: если вы открыли файл для записи, закрытие его сначала "очистит свой выходной буфер". Это означает, что библиотеки i/o оптимизируют использование диска путем сбора ( "буферизации" ) того, что вы выписываете, и сохранения его на диск партиями. Если вы пишете текст в файл и сразу же пытаетесь открыть и прочитать его, не закрыв первый дескриптор вывода, вы обнаружите, что не все было записано. Кроме того, если ваша программа слишком быстро закрывается (с сигналом или иногда даже через обычный выход), выход никогда не будет кратковременным.

Уже есть много других ответов на вопрос о том, как выпустить файлы, так что вот лишь краткий список подходов:

  • Явно с close(). (Примечание для новичков python: не забывайте о parens! Мои ученики любят писать in_file.close, который ничего не делает.)

  • Рекомендуем: Неявно, открывая файлы с помощью оператора with. Метод close() вызывается, когда достигается конец блока with, даже в случае аномального завершения (из исключения).

    with open("data.txt") as in_file:
        data = in_file.read()
    
  • Неявно с помощью диспетчера ссылок или сборщика мусора, если ваш движок python его реализует. Это не рекомендуется, поскольку оно не полностью переносимо; см. другие ответы для деталей. Вот почему оператор with был добавлен в python.

  • Неявно, когда заканчивается ваша программа. Если файл открыт для вывода, это может привести к выходу программы, прежде чем все будет сброшено на диск.

Ответ 2

Питонический способ справиться с этим - использовать with контекстный менеджер:

with open(from_file) as in_file, open(to_file, 'w') as out_file:
    indata = in_file.read()
    out_file.write(indata)

Используется с такими файлами, with гарантирует, что для вас будет произведена вся необходимая очистка, даже если ошибки read() или write() выдают.

Ответ 3

По умолчанию интерпретатор python, CPython, использует подсчет ссылок. Это означает, что, когда нет ссылок на объект, он получает сбор мусора, то есть очищается.

В вашем случае, делая

open(to_file, 'w').write(indata)

создаст файл-объект для to_file, но не присвоит его имени - это означает, что ссылки на него нет. Вы не можете манипулировать объектом после этой строки.

CPython обнаружит это и очистит объект после его использования. В случае файла это означает автоматическое закрытие. В принципе, это нормально, и ваша программа не будет утечка памяти.

"Проблема" - это механизм реализации интерпретатора CPython. Стандарт языка явно не гарантирует этого! Если вы используете альтернативный интерпретатор, такой как pypy, автоматическое закрытие файлов может задерживаться на неопределенный срок. Это включает в себя другие неявные действия, такие как сброс записи при закрытии.

Эта проблема также относится к другим ресурсам, например. сетевые сокеты. Хорошей практикой всегда явно обращаться с такими внешними ресурсами. Начиная с python 2.6, оператор with делает это элегантным:

with open(to_file, 'w') as out_file:
    out_file.write(in_data)

TL;DR: он работает, но, пожалуйста, не делайте этого.

Ответ 4

Ответы до сих пор абсолютно правильны при работе на python. Вы должны использовать контекстный менеджер with open(). Это отличная встроенная функция и помогает сократить общую задачу программирования (открытие и закрытие файла).

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

Первая версия вашего кода отлично. Вы открываете файл, сохраняете ссылку, читаете ее из файла и закрываете. Вот как написано много кода, когда язык не предоставляет ярлык для задачи. Единственное, что я хотел бы улучшить, - переместить close() туда, где вы открываете и читаете файл. После открытия и чтения файла у вас есть содержимое в памяти и больше не нужно, чтобы файл был открыт.

in_file = open(from_file)
indata = in_file.read()
out_file.close() 

out_file = open(to_file, 'w')
out_file.write(indata)
in_file.close()

Ответ 5

Хорошо использовать ключевое слово with при работе с файловыми объектами. Это имеет то преимущество, что файл правильно закрыт после завершения его набора, даже если в пути возникло исключение. Это также намного короче, чем запись эквивалентных блоков try-finally:

>>> with open('workfile', 'r') as f:
...     read_data = f.read()
>>> f.closed
True

Ответ 6

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

with open(from_file, 'r') as in_file:
    in_data = in_file.read()

with open(to_file, 'w') as out_file:
    outfile.write(in_data)