Коллекции стандартной библиотеки: deque, Counter, defaultdict и ChainMap
Содержание курса
Слоистая конфигурация и границы записи в ChainMap
В приложениях конфигурация часто собирается из нескольких источников с разным приоритетом: флаги командной строки переопределяют переменные окружения, а те переопределяют значения по умолчанию. При объединении таких настроек через dict.update() мутируется исходный словарь-получатель, а оператор слияния | создаёт новый объединённый словарь. В обоих случаях плоский результат сам по себе не сохраняет структуру исходных слоёв.
collections.ChainMap решает эту задачу иначе: он группирует несколько словарей в единый логический контекст поиска, сохраняя ссылки на исходные отображения без копирования данных. Поиск ключа выполняется по цепочке слева направо (от первого переданного словаря к последнему):
from collections import ChainMap
defaults = {"host": "localhost", "port": 8000, "debug": False}
env_vars = {"port": 5432, "debug": True}
cli_args = {"debug": False}
config = ChainMap(cli_args, env_vars, defaults)
print("host:", config["host"]) # взято из defaults
print("port:", config["port"]) # взято из env_vars
print("debug:", config["debug"]) # взято из cli_args
Вывод программы:
host: localhost
port: 5432
debug: False
При этом поведение ChainMap при записи имеет строгое ограничение: любые изменения (config[key] = value, del config[key], pop()) применяются исключительно к первому словарю в цепочке — config.maps[0].
Отсюда возникает частая ошибка при попытке удалить параметр, заданный только во вложенном слое:
config = ChainMap(cli_args, env_vars, defaults)
try:
# Ключ 'host' виден в config, но физически лежит в defaults (слой 2)
del config["host"]
except KeyError as e:
print("Ошибка удаления:", repr(e))
Вывод программы:
Ошибка удаления: KeyError("Key not found in the first mapping: 'host'")
Интерпретатор проверяет наличие ключа только в config.maps[0]. Поскольку в первом словаре cli_args ключа 'host' нет, операция немедленно выбрасывает KeyError, не пытаясь менять нижние слои. Это гарантирует неизменность базовых конфигураций, но требует помнить: ChainMap управляет лишь верхним слоем контекста, а не распределяет операции записи по всей глубине списка maps.
