Запуск питона через входной файл

Мой вопрос связан с вводом файлов в Python, используя open(). У меня есть текстовый файл mytext.txt с 3 строками. Я пытаюсь сделать две вещи с этим файлом: напечатать строки и напечатать количество строк.

Я попробовал следующий код:

input_file = open('mytext.txt', 'r')
count_lines = 0
for line in input_file:
    print line
for line in input_file:
    count_lines += 1
print 'number of lines:', count_lines

Результат: он правильно печатает 3 строки, но печатает "количество строк: 0" (вместо 3)


Я нашел два способа его решения и распечатал 3:

1) Я использую один цикл вместо двух

input_file = open('mytext.txt', 'r')
count_lines = 0
for line in input_file:
    print line
    count_lines += 1
print 'number of lines:', count_lines

2) после первого цикла, я снова определяю input_file

input_file = open('mytext.txt', 'r')
count_lines = 0
for line in input_file:
    print line
input_file = open('mytext.txt', 'r')
for line in input_file:
    count_lines += 1
print 'number of lines:', count_lines

Мне кажется, что определение input_file = ... допустимо только для одного цикла, как если бы он был удален после использования его для цикла. Но я не понимаю, почему, возможно, мне это пока еще не ясно, как variable = open(filename) обрабатывается в Python.

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

Ответ 1

Дескриптор файла - это итератор. После итерации по файлу указатель будет помещен в EOF (конец файла), и итератор будет вызывать StopIteration, который выходит из цикла. Если вы попытаетесь использовать итератор для файла, в котором указатель находится в EOF, он просто поднимет StopIteration и выйдет: вот почему во втором цикле он равен нулю. Вы можете перематывать указатель файла с помощью input_file.seek(0) без его повторного открытия.

Тем не менее, подсчет строк в одном и том же цикле является более эффективным вводом-выводом, иначе вам придется читать весь файл с диска во второй раз, только чтобы подсчитать строки. Это очень распространенная картина:

with open('filename.ext') as input_file:
    for i, line in enumerate(input_file):
        print line,
print "{0} line(s) printed".format(i+1)

В Python 2.5 файловый объект был оснащен __enter__ и __exit__, чтобы обратиться к with statement interface. Это синтаксический сахар для чего-то вроде:

input_file = open('filename.txt')
try:
    for i, line in enumerate(input_file):
        print line,
finally:
    input_file.close()
print "{0} line(s) printed".format(i+1)

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

Ответ 2

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

input_file = open('mytext.txt', 'r')
count_lines = 0
for line in input_file:
    print line
    count_lines += 1
print 'number of lines:', count_lines

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

Ответ 3

Попробуйте добавить input_file.seek(0) между двумя циклами. Это перемотает файл назад в начало, так что вы можете снова перебрать его.

Ответ 4

Я удаляю файл fileinput модуля.

Вот ссылка

if __name__ == "__main__":
for line in fileinput.input():
    if fileinput.isfirstline():
        print("current file: %s" % fileinput.filename())

    print("line number: %d, current file number: %d" % 
          (fileinput.lineno(), fileinput.filelineno()))