Есть ли необходимость закрывать файлы, которые не имеют ссылки на них?


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

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. Однако оставляет ли это меня с двумя файлами, которые открыты, но ничего не имеют к ним отношения? Как мне закрыть их, или в этом нет необходимости?

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

6   51  

6 ответов:

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

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

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

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

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

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

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

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

питонический способ справиться с этим-использовать 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() выдавать ошибки.

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

в вашем случае, делать

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

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

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

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

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

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

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

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

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

ответы до сих пор абсолютно правильно при работе в 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()

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

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)