История последних событий растёт бесконечно: ограничиваем deque

Для диагностики компонент хранит последние сообщения в списке. Изначально предполагалось показывать только двадцать строк, но ограничили лишь отображение через history[-20:]. Сам список продолжает расти. Через долгую работу в памяти остаётся история, которую никто уже не читает.

Если задача действительно состоит в сохранении последних N элементов, deque с maxlen выражает это прямо в структуре данных. При добавлении справа в заполненный буфер старый элемент слева автоматически удаляется. Для просмотра можно получить список, не меняя сам буфер. Не нужно после каждого append вычислять, какой срез удалить.

История с фиксированной вместимостью

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

from collections import deque

history: deque[str] = deque(maxlen=3)
for event in ["starting", "connected", "received", "closed"]:
    history.append(event)

snapshot = list(history)
assert snapshot == ["connected", "received", "closed"]
assert len(history) == 3
snapshot.clear()
assert len(history) == 3
print(*history, sep=", ")

Что именно ограничивает maxlen

Файл работает на Python 3.12 без дополнительных пакетов и печатает connected, received, closed. В отличие от списка с последующим срезом размер самого контейнера ограничен. При этом maxlen ограничивает количество элементов, а не суммарный размер памяти. Если один элемент содержит мегабайты текста, небольшой N всё равно может означать большой буфер.

Для такой истории полезно хранить компактную запись: время, код события, короткие поля диагностики. Огромное тело ответа лучше не удерживать только потому, что оно доступно в момент события. Решение о составе записи влияет на память сильнее, чем замена одного контейнера другим. Если требуется точное ограничение по байтам, одного maxlen недостаточно.

Ограниченная deque намеренно теряет старые элементы. Поэтому это подходящий инструмент для панели последних событий, но не журнал аудита, не очередь обязательной доставки и не хранилище результатов. У bounded deque даже поведение insert отличается от append: вставка, которая переполнила бы буфер, поднимает IndexError. Не стоит делать универсальный интерфейс списка, не разобрав используемые операции.

В тестах проверьте пустой буфер, ровно N добавлений и N+1. Если наружу возвращается list(history), изменение этого списка не должно менять внутреннюю историю; добавьте и такую проверку интерфейса. Для нескольких потоков составные действия вроде «проверить последний и добавить» требуют отдельной синхронизации. Ограничение вместимости и согласованность операций — разные свойства, каждое должно соответствовать реальному использованию компонента.

Когда локальный пример работает, полезно разобрать соседние механизмы языка и их взаимодействие. В курсе «Python для профессионалов» есть практика по итераторам, генераторам, декораторам, asyncio, метапрограммированию и профилированию. Эти темы помогают понимать время жизни объектов, управление выполнением и причины поведения прикладной программы.

Посмотреть программу и практику курса.

Документация: официальный источник по теме.

Другие задачи по теме: Типы, тесты и поведение программы.

Продолжить на практике

  • Python для профессионалов ↗

    Python для профессионалов — курс для людей, стремящихся углубить свои знания языка. Вы изучите итераторы, генераторы, декораторы, современные возможности Python (3.8+), освоите метапрограммирование, асинхронность, внутреннее устройство интерпретатора, инструменты оптимизации и профилирования, а также получите уверенность в использовании продвинутых возможностей стандартной библиотеки.