Чтение больших файлов в Python: эффективное использование yield
Быстрый ответ
Для прочтения больших файлов в Python наиболее эффективно использовать with
и for
следующим образом:
with open('huge_file.txt') as file:
for line in file:
process(line) # замените на функцию обработки строк
Замените process(line)
на вашу функцию обработки. Этот метод позволяет снижать потребление памяти, загружая в память только одну строку за раз.
Если вам нужно изменить размер обрабатываемых блоков данных или добавить дополнительную обработку, используйте генератор с yield
:
def read_in_chunks(file_path, chunk_size=1024*1024):
with open(file_path, 'r') as file:
while True:
data = file.read(chunk_size)
if not data:
break
yield data
С его помощью чтение будет выглядеть следующим образом:
for chunk in read_in_chunks('huge_file.txt'):
process(chunk) # место для ваших обработчиков данных
Этот подход хорошо подходит как для работы с бинарными файлами, так и при обработке текстов без ориентиров на строки.
Большие бинарные файлы? Не проблема
Для работы с бинарными файлами или изображениями, где считывание построчно неэффективно, лучше использовать чтение данных блоками фиксированного размера. При задании размера блока необходимо учитывать параметры системы для оптимизации чтения и избежания нехватки памяти.
Работа с текстовыми файлами и нестандартными разделителями
Если в текстовых файлах используются нестандартные разделители, потребуется функция для правильного чтения данных под эти разделители:
def custom_readline(f, delimiter='\n'):
buffer = []
while True:
chunk = f.read(4096)
if not chunk:
yield ''.join(buffer)
break
buffer.append(chunk)
position = ''.join(buffer).find(delimiter)
if position != -1:
yield ''.join(buffer)[:position]
buffer = [''.join(buffer)[position + len(delimiter):]]
Метод mmap для доступа к файлам
На 64-битных системах можно использовать модуль mmap
для обхода ограничений по памяти. Такой подход увеличит скорость обработки больших файлов. Однако стоит внимательно относиться к проблемам адресации на 32-битных системах:
import mmap
with open('huge_file.txt', mode='r') as f:
mm = mmap.mmap(f.fileno(), length=0, access=mmap.ACCESS_READ)
for line in iter(mm.readline, b''):
process(line) # место для функции обработки
mm.close()
Визуализация
При ленивом чтении файла вы проходите следующий путь:
Большой файл: [🥩🍗🍉🍰🧁🍔🍟🌭🥗🍲🍱...]
Ленивый подход позволяет читать данные по частям:
with open('big_file.txt', 'r') as file:
while True:
bite = file.readline() # читаем кусок данных!
if not bite:
break # достигли конец файла
# обработка куска
При каждом вызове readline()
вы получаете новый кусок данных, готовый к обработке.
Контролируйте поток данных с помощью буфера
Указывая размер буфера в функции open()
, можно управлять объемом данных, считываемых за раз. Это актуально при работе с медленными сетями или большими данными:
buffer_size = 64 # меньше вес – больше скорость!
with open('big_file.txt', 'rb', buffering=buffer_size) as file:
for bite in iter(lambda: file.read(buffer_size), b''):
process(bite) # обработка каждого куска
Краткость – сестра таланта: выражения присваивания
С Python 3.8 появилось выражение присваивания, которое сделает цикл более лаконичным и элегантным:
with open('huge_file.txt', 'r') as file:
while (line := file.readline()):
process(line) # обработка строки
Чуть-чуть эксперимента: изменяем размер части
Пробуя разные размеры частей данных, можно найти оптимальное соотношение производительности и потребления памяти:
chunk_sizes = [1024, 2048, 4096, 8192, 16384]
for size in chunk_sizes:
start_time = time.time()
for chunk in read_in_chunks('huge_file.txt', chunk_size=size):
process(chunk)
print(f"Обработка части размером {size} заняла {time.time() – start_time} секунд")
Безопасность превыше всего: сохраняем обработанные данные
Чтобы предотвратить потерю данных в случае сбоя, сохраняйте каждый обработанный блок в отдельном файле или базе данных. Это также упростит возобновление обработки после остановки.