Чтение больших файлов в Python: эффективное использование yield

Пройдите тест, узнайте какой профессии подходите

Я предпочитаю
0%
Работать самостоятельно и не зависеть от других
Работать в команде и рассчитывать на помощь коллег
Организовывать и контролировать процесс работы

Быстрый ответ

Для прочтения больших файлов в Python наиболее эффективно использовать with и for следующим образом:

Python
Скопировать код
with open('huge_file.txt') as file:
    for line in file:
        process(line)  # замените на функцию обработки строк

Замените process(line) на вашу функцию обработки. Этот метод позволяет снижать потребление памяти, загружая в память только одну строку за раз.

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

Python
Скопировать код
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

С его помощью чтение будет выглядеть следующим образом:

Python
Скопировать код
for chunk in read_in_chunks('huge_file.txt'):
    process(chunk)  # место для ваших обработчиков данных

Этот подход хорошо подходит как для работы с бинарными файлами, так и при обработке текстов без ориентиров на строки.

Кинга Идем в IT: пошаговый план для смены профессии

Большие бинарные файлы? Не проблема

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

Работа с текстовыми файлами и нестандартными разделителями

Если в текстовых файлах используются нестандартные разделители, потребуется функция для правильного чтения данных под эти разделители:

Python
Скопировать код
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-битных системах:

Python
Скопировать код
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()

Визуализация

При ленивом чтении файла вы проходите следующий путь:

Markdown
Скопировать код
Большой файл: [🥩🍗🍉🍰🧁🍔🍟🌭🥗🍲🍱...]

Ленивый подход позволяет читать данные по частям:

Python
Скопировать код
with open('big_file.txt', 'r') as file:
    while True:
        bite = file.readline()  # читаем кусок данных!
        if not bite:
            break  # достигли конец файла
        # обработка куска

При каждом вызове readline() вы получаете новый кусок данных, готовый к обработке.

Контролируйте поток данных с помощью буфера

Указывая размер буфера в функции open(), можно управлять объемом данных, считываемых за раз. Это актуально при работе с медленными сетями или большими данными:

Python
Скопировать код
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 появилось выражение присваивания, которое сделает цикл более лаконичным и элегантным:

Python
Скопировать код
with open('huge_file.txt', 'r') as file:
    while (line := file.readline()):
        process(line)  # обработка строки

Чуть-чуть эксперимента: изменяем размер части

Пробуя разные размеры частей данных, можно найти оптимальное соотношение производительности и потребления памяти:

Python
Скопировать код
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} секунд")

Безопасность превыше всего: сохраняем обработанные данные

Чтобы предотвратить потерю данных в случае сбоя, сохраняйте каждый обработанный блок в отдельном файле или базе данных. Это также упростит возобновление обработки после остановки.

Полезные материалы