Чтение больших файлов в 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} секунд")
Безопасность превыше всего: сохраняем обработанные данные
Чтобы предотвратить потерю данных в случае сбоя, сохраняйте каждый обработанный блок в отдельном файле или базе данных. Это также упростит возобновление обработки после остановки.


