В доисторические времена (Python 1.4) мы делали:
fp = open('filename.txt')
while 1:
line = fp.readline()
if not line:
break
print line
после Python 2.1 мы сделали:
for line in open('filename.txt').xreadlines():
print line
прежде чем мы получили удобный протокол итератора в Python 2.3, и могли бы сделать:
for line in open('filename.txt'):
print line
Я видел несколько примеров, использующих более подробный:
with open('filename.txt') as fp:
for line in fp:
print line
это предпочтительный метод продвижения вперед?
[править] Я понял, что оператор with обеспечивает закрытие файла ... но почему это не включено в протокол итератора для файловых объектов?
python
python-3.x
python-2.7
thebjorn
источник
источник
Ответы:
Существует ровно одна причина, почему предпочтительнее следующее:
Мы все избалованы относительно детерминированной схемой подсчета ссылок CPython для сборки мусора. Другие гипотетические реализации Python не обязательно закрывают файл «достаточно быстро» без
with
блока, если они используют какую-то другую схему для освобождения памяти.В такой реализации вы можете получить ошибку «слишком много открытых файлов» из ОС, если ваш код открывает файлы быстрее, чем сборщик мусора вызывает финализаторы для потерянных дескрипторов файлов. Обычный обходной путь заключается в немедленном срабатывании GC, но это неприятный хак, и его должны выполнять все функции, которые могут столкнуться с ошибкой, в том числе в библиотеках. Какой кошмар.
Или вы можете просто использовать
with
блок.Бонусный вопрос
(Перестаньте читать сейчас, если вас интересуют только объективные аспекты вопроса.)
Это субъективный вопрос о разработке API, поэтому у меня есть субъективный ответ в двух частях.
На уровне инстинкта это кажется неправильным, потому что он заставляет протокол итератора выполнять две отдельные вещи - перебирать строки и закрывать дескриптор файла - и часто плохая идея заставить простую на вид функцию выполнять два действия. В этом случае это особенно плохо, потому что итераторы связывают квази-функциональный, основанный на значениях способ с содержимым файла, но управление дескрипторами файла является совершенно отдельной задачей. Сокращение обоих, невидимо, в одно действие, удивляет людей, которые читают код, и затрудняет рассуждения о поведении программы.
Другие языки по сути пришли к такому же выводу. Haskell кратко заигрывает с так называемым «ленивым вводом-выводом», который позволяет вам перебирать файл и автоматически закрывать его, когда вы достигаете конца потока, но в наши дни почти повсеместно не рекомендуется использовать отложенный ввод-вывод в Haskell, и Haskell пользователи в основном перешли на более явное управление ресурсами, например Conduit, которое больше похоже на
with
блок в Python.На техническом уровне есть некоторые вещи, которые вы можете захотеть сделать с дескриптором файла в Python, который не будет работать так же хорошо, если итерация закроет дескриптор файла. Например, предположим, мне нужно дважды перебрать файл:
Хотя это менее распространенный вариант использования, учтите тот факт, что я мог бы просто добавить три строки кода внизу к существующей базе кода, которая изначально имела три верхние строки. Если бы итерация закрыла файл, я бы не смог этого сделать. Таким образом, разделение итерации и управления ресурсами облегчает объединение кусков кода в большую работающую программу на Python.
Композиционность является одной из наиболее важных функций юзабилити языка или API.
источник
with
действительно дает вам душевное спокойствие, так что это все еще лучшая практика.Да,
это путь
Это не более многословно. Это более безопасно.
источник
если вы отключены дополнительной строкой, вы можете использовать функцию-обертку следующим образом:
в Python 3.3 это
yield from
утверждение сделает это еще короче:источник
источник