Как я должен читать файл построчно в Python?


в доисторические времена (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

это предпочтительный метод, идущий вперед?

[edit] я получаю, что оператор with обеспечивает закрытие файла... но почему это не включено в протокол итератора для файловых объектов?

4 111

4 ответа:

существует ровно одна причина, почему предпочтительно следующее:

with open('filename.txt') as fp:
    for line in fp:
        print line

мы все испорчены относительно детерминированной схемой подсчета ссылок CPython для сбора мусора. Другие, гипотетические реализации Python не обязательно закроют файл "достаточно быстро" без with блок, если они используют какую-то другую схему для восстановления памяти.

в такой реализации вы можете получить ошибку "слишком много файлов открыто" из ОС, если ваш код открывается файлы быстрее, чем сборщик мусора вызывает завершители для потерянных дескрипторов файлов. Обычный обходной путь-немедленно запустить GC, но это неприятный Хак, и это должно быть сделано каждый функция, которая может столкнуться с ошибкой, в том числе в библиотеках. Какой кошмар.

или вы могли бы просто использовать with блок.

Дополнительный Вопрос

(прекратите читать сейчас, если вас интересуют только объективные аспекты вопрос.)

почему это не включено в протокол итератора для файловых объектов?

это субъективный вопрос о дизайне API, поэтому у меня есть субъективный ответ в двух частях.

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

другие языки, по существу, пришли к тому же выводу. Haskell кратко флиртовал с так называемым "ленивым IO", который позволяет вам перебирать файл и иметь его автоматически закрывается, когда вы добираетесь до конца потока, но в наши дни почти повсеместно не рекомендуется использовать lazy 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.

да

with open('filename.txt') as fp:
    for line in fp:
        print line

- это путь.

Это не более подробно. Это более безопасно.

Если вы отключены дополнительной строкой, вы можете использовать функцию обертки следующим образом:

def with_iter(iterable):
    with iterable as iter:
        for item in iter:
            yield item

for line in with_iter(open('...')):
    ...

в Python 3.3,yield from заявление сделает это еще короче:

def with_iter(iterable):
    with iterable as iter:
        yield from iter
f = open('test.txt','r')
for line in f.xreadlines():
    print line
f.close()