Ответ 1
Существует только одна причина, по которой предпочтительнее следующее:
with open('filename.txt') as fp:
for line in fp:
print line
Мы все испорчены CPython относительно детерминированной схемой подсчета ссылок для сбора мусора. Другие гипотетические реализации Python не обязательно будут закрывать файл "достаточно быстро" без блока with
, если они используют какую-то другую схему для восстановления памяти.
В такой реализации вы можете получить сообщение об ошибке "слишком много файлов" из ОС, если ваш код открывает файлы быстрее, чем сборщики мусора собирают финализаторы в обработанных сиротских файлах. Обычное обходное решение - немедленно запустить GC, но это неприятный взлом, и это нужно сделать с помощью каждой функции, которая может столкнуться с ошибкой, в том числе в библиотеках. Какой кошмар.
Или вы можете просто использовать блок with
.
Бонусный вопрос
(перестаньте читать, если интересуетесь только объективными аспектами вопроса.)
Почему это не включено в протокол итератора для файловых объектов?
Это субъективный вопрос об дизайне API, поэтому у меня есть субъективный ответ в двух частях.
На уровне кишки это кажется неправильным, потому что он делает протокол итератора двумя отдельными вещами - перебирает по линиям и закрывает дескриптор файла, и часто бывает плохой идеей сделать простую функцию для выполнения двух действий. В этом случае он чувствует себя особенно плохо, потому что итераторы относятся к квази-функциональному, основанному на значении способу к содержимому файла, но управление файловыми дескрипторами - это совершенно отдельная задача. Сжатие обоих, невидимо, в одно действие, удивительно для людей, которые читают код и затрудняют рассуждение о поведении программы.
Другие языки по существу пришли к одному и тому же выводу. Haskell ненадолго флиртовал с так называемым "ленивым IO", который позволяет вам перебирать файл и автоматически закрывать его, когда вы добираетесь до конца потока, но почти повсеместно не рекомендуется использовать ленивый IO в Haskell в эти дни, а Haskell пользователи в основном перешли к более явному управлению ресурсами, например, Conduit, который больше похож на блок with
на Python.
На техническом уровне есть некоторые вещи, которые вы можете захотеть сделать с дескриптором файла в Python, который не будет работать, если итерация закрыла дескриптор файла. Например, предположим, что мне нужно дважды перебирать файл:
with open('filename.txt') as fp:
for line in fp:
...
fp.seek(0)
for line in fp:
...
В то время как это менее распространенный случай использования, рассмотрим тот факт, что я мог бы просто добавить три строки кода внизу к существующей базе кода, изначально имевшей три верхние строки. Если итерация закрыла файл, я бы не смог этого сделать. Таким образом, сохранение итераций и управления ресурсами в отдельности упрощает компоновку фрагментов кода в более крупную рабочую программу Python.
Совместимость - одна из самых важных функций удобства использования языка или API.