← Модуль itertools · 🏠 Домой · Сортировка →
Коротко. При обращении к отсутствующему ключу он не бросает KeyError,
а создаёт значение вызовом фабрики, переданной в конструктор, и сохраняет его
в словаре.
from collections import defaultdict
groups = defaultdict(list)
groups["a"].append(1) # ключа не было — создался пустой список
print(dict(groups))
# {'a': [1]}Фабрика — любой вызываемый объект без аргументов: list, set, int
(даёт 0 — удобно для счётчиков), lambda: "n/a".
Подвох. Чтение тоже создаёт ключ — это главный источник сюрпризов:
counts = defaultdict(int)
if counts["missing"] > 0: # просто посмотрели
...
print(dict(counts))
# {'missing': 0} — ключ теперь естьЕсли побочный эффект не нужен, берут dict.get(key, default) или
dict.setdefault(key, []). Разница между ними: setdefault вычисляет
дефолтное значение всегда, даже когда ключ есть, а defaultdict — только
при промахе.
Глубже. Механика — метод __missing__, который dict.__getitem__
вызывает при отсутствии ключа. Его можно определить и в своём подклассе
dict; defaultdict — просто готовая реализация. Обратите внимание,
что __missing__ не участвует в get() и в in.
Коротко. Это подкласс dict, считающий вхождения: ключ → количество.
Отсутствующий ключ даёт 0, а не KeyError, плюс есть most_common()
и арифметика мультимножеств.
from collections import Counter
c = Counter("abracadabra")
c.most_common(2) # [('a', 5), ('b', 2)]
c["z"] # 0 — и ключ при этом НЕ создаётсяCounter("aab") - Counter("ab") # Counter({'a': 1}) — отрицательные отброшены
Counter("aab") + Counter("b") # Counter({'a': 2, 'b': 2})Подвох. - и + выбрасывают неположительные значения, а subtract() —
нет, он честно оставит отрицательные счётчики. Это разные операции, и на
собеседовании обычно спрашивают именно про -.
Коротко. Когда добавляют или удаляют элементы с обоих концов. У deque
это O(1) с каждой стороны, у списка insert(0, x) и pop(0) — O(n),
потому что весь массив указателей сдвигается.
from collections import deque
dq = deque([1, 2, 3], maxlen=3)
dq.appendleft(0)
print(dq)
# deque([0, 1, 2], maxlen=3) — 3 вытеснено справаmaxlen даёт готовое кольцо фиксированной длины (окно последних N событий,
«последние 100 строк лога»), rotate(n) циклически сдвигает содержимое.
Подвох. Плата за быстрые концы — доступ по индексу: dq[i] в середине
это O(n), а срезы dq[1:3] вообще не поддерживаются. deque — очередь,
а не замена списку.
Глубже. Внутри — двусвязный список блоков по 64 указателя, а не один
непрерывный массив, как у list. Отсюда и O(1) на концах, и потеря
произвольного доступа. deque потокобезопасен для append/popleft —
атомарность обеспечивает сама реализация на уровне C-кода. В free-threaded
сборке (3.13t/3.14t, без GIL) та же атомарность сохраняется, но уже через
явные per-object критические секции, добавленные в реализацию deque
именно под no-GIL сборку, а не через GIL, которого там нет. Поэтому deque
берут как простую очередь между потоками, когда не нужны блокировки
queue.Queue.
Коротко. namedtuple — это настоящий кортеж с именами полей:
неизменяемый, хешируемый, распаковывается, сравнивается поэлементно и не
занимает лишней памяти. dataclass — изменяемый объект с __dict__
(или __slots__) и куда более гибкий.
from collections import namedtuple
Point = namedtuple("Point", "x y")
p = Point(1, 2)
p.x # 1
p._replace(y=5) # Point(x=1, y=5) — новый объект, старый не изменился
p._asdict() # {'x': 1, 'y': 2}
isinstance(p, tuple) # TrueПодчёркивание в _replace/_asdict — не «приватность», а защита от
конфликта с именами полей.
Подвох. Раз это кортеж — он равен обычному кортежу с теми же значениями
(Point(1, 2) == (1, 2) истинно) и его можно случайно распаковать не в том
порядке. Когда нужна строгая типизация полей, берут typing.NamedTuple
(тот же кортеж, но с аннотациями) или dataclass
(см. Классы).
-
OrderedDict— до Python 3.7 единственный словарь с гарантированным порядком. Сейчас порядок вставки гарантирован и у обычногоdict, ноOrderedDictостаётся полезен: у него естьmove_to_end()иpopitem(last=), а сравнение учитывает порядок:from collections import OrderedDict OrderedDict(a=1, b=2) == OrderedDict(b=2, a=1) # False — порядок важен dict(a=1, b=2) == dict(b=2, a=1) # True
-
ChainMap— цепочка словарей как один: поиск идёт по слоям до первого попадания, записи уходят в первый слой. Типовое применение — слои конфигурации (аргументы → окружение → умолчания) без копирования словарей.