Допустим, у нас есть текстовый файл student_records.txt. Каждая строка содержит имя и возраст учащегося. Мы хотим проанализировать имена учащихся и их возраст.
#student_records.txt greg:15 matthew:14 ram:16 raju:14
Нашим первым подходом было бы чтение файлов и сохранение значений в списке / словаре.
def read_student_records(path): records = [] with open(path) as file: for line in file: name, age = line.split(":") records.append((name, age)) return records for record in read_student_records(path): print(record) # do something with record
Это выполняет свою работу. Однако функция read_student_records () беспорядочная. Что, если нам нужно читать из нескольких файлов?
Давайте очистим наш код выше и сделаем его более модульным.
def lines_from_file(path): lines = [] with open(path) as file: for line in file: lines.append(line) return lines def student_records(lines): records = [] for line in lines: name, age = line.split(":") records.append((name, age)) return records def student_records_from_file(path): lines = lines_from_file(path) # 1 records = student_records(lines) # 2 return records # read from multiple files and do something with it def student_records_from_files(filenames): records = [] for filename in filenames: records_from_file = student_records_from_file(filename) # 3 records.extend(records_from_file) return records filenames = ['r1.txt', 'r2.txt'] records = student_records_from_multiple_files(filenames) for record in records: print(record) # do something with record
Вышеупомянутые функции говорят сами за себя.
- Функция lines_from_file считывает файл построчно, сохраняет его в списке и возвращает.
- student_records читает список строк, извлекает имя и возраст из каждой строки и сохраняет его в списке.
- student_records_from_file объединяет lines_from_file и student_records.
- student_records_from_files вызывает student_records_from_file, сохраняет записи и возвращает их.
Кажется, все в порядке? Не могли бы вы выяснить некоторые проблемы в приведенном выше коде? Я буду ждать.
В функции student_records_from_file
- # 1 блоки, пока не будут прочитаны все строки.
- # 2 блока для разбора каждой записи.
- # 3 блока для чтения всех записей из файла перед переходом к следующему.
В каждой из этих функций мы храним данные и выбрасываем их, когда возвращаем значения другой функции.
Что, если в некоторых файлах содержится 1 миллиард записей или более?
Нам не нужно хранить все строки в памяти перед их анализом. Желательно перебирать каждую строку и анализировать их, по одной строке в памяти за раз.
Генераторы помогут вам в этом. Это очень полезный инструмент для создания итераторов. Генераторы отличаются от обычных функций Python тем, что они yield вместо return.
Итератор создает значения в последовательности, по одному за раз. Вызов next () на итераторе возвращает следующее значение в последовательности.
А теперь вернемся к генераторам. Есть разница между функцией генератора и объектом-генератором. Когда вы определяете функцию, которая «дает», а не «возвращает», это функция-генератор. Когда вы вызываете функцию-генератор, создается объект-генератор. Выполнение не происходит до тех пор, пока для объекта-генератора не будет вызвана функция next ().
Давайте сейчас перепишем наш код с помощью генераторов.
def lines_from_file(path): with open(path) as file: for line in file: yield line def student_records(lines): for line in lines: name, age = line.split(":") yield name, age def student_records_from_file(path): lines = lines_from_file(path) yield from student_records(lines) def student_records_from_files(filenames): for file in filenames: yield from student_records_from_file(file) # 4
yield from - это ярлык для получения данных из другого объекта-генератора:
#4 can be also be written as for record in student_records_from_file(file): yield record
Теперь вы можете перебирать записи учеников одну за другой и что-то с ними делать.
for record in student_records_from_files(filenames): print(record) #do something with record
Это приводит к простой модульной конструкции. Каждая функция отвечает за выполнение одной задачи, поэтому она оптимизирована для повторного использования. Кроме того, он занимает мало места в памяти, поскольку в каждый момент времени в памяти хранится одна запись об учащемся.