2.37. Именованные кортежи и двусторонние очереди¶
Списки, кортежи, словари и множества покрывают большинство потребностей в данных. Три других контейнера в модуле collections подходят для конкретных задач, которые встроенные типы решают неуклюже.
2.37.1. namedtuple – типизированные записи без класса¶
Обычный кортеж хранит значения по позиции. Это нормально для маленького, недолговечного кортежа из 2 или 3 элементов, но дальше point[0] и point[1] начинают вводить в заблуждение относительно того, что они содержат. collections.namedtuple() возвращает новый подкласс кортежа, поля которого имеют имена:
>>> from collections import namedtuple
>>> Reading = namedtuple('Reading', ('temp', 'humidity', 'ts'))
>>> r = Reading(22.5, 41.0, 137204)
>>> r.temp
22.5
>>> r.humidity
41.0
>>> r[0]
22.5
Аргумент полей – это последовательность строк-имён (или одна строка с именами через пробел в CPython; MicroPython строже – передавайте кортеж или список).
Зачем использовать namedtuple вместо класса?
Это кортеж. Итерация, распаковка, сравнение на равенство, хеширование и использование в качестве ключа словаря – всё работает бесплатно.
Он неизменяем. Переприсваивание
r.temp = ...вызываетAttributeError, что именно то, что нужно для типа-записи.Он расходует меньше ОЗУ, чем экземпляр класса с теми же полями – хранилище кортежа непрерывно, без
__dict__.
По сравнению с эквивалентным классом объявление namedtuple занимает одну строку. Компромисс в том, что поля доступны только для чтения – чтобы «изменить» показание, вы создаёте новое.
2.37.2. deque – ограниченный кольцевой буфер¶
Список быстр на конце (append / pop) и медлен в начале (insert(0, ...) / pop(0) оба сдвигают каждый остальной элемент). collections.deque быстра на обоих концах – это кольцевой буфер, индексируемый указателями головы и хвоста, поэтому добавление и извлечение с любой стороны выполняются за одинаковый фиксированный объём работы независимо от того, сколько элементов содержит deque.
Создание в MicroPython требует как начального итерируемого объекта, так и максимальной длины, именно в этом порядке:
>>> from collections import deque
>>> events = deque((), 5)
>>> for i in range(8):
... events.append(i)
>>> list(events)
[3, 4, 5, 6, 7]
Когда ограниченная deque заполнена, каждый append отбрасывает самый старый элемент. Это делает deque идеальной для «последних N выборок», «последних N строк журнала» или любого скользящего окна, которому не нужно расти бесконечно.
Методы, доступные в deque MicroPython, намеренно минимальны:
append(x)– добавить справа.appendleft(x)– добавить слева.extend(iterable)– добавить каждый элемент из итерируемого объекта.pop()– удалить и вернуть правый конец. ВызываетIndexError, если пусто.popleft()– удалить и вернуть левый конец.
Заметные отсутствия по сравнению с deque из CPython: нет clear, count, index, remove, reverse, rotate, атрибута maxlen или __contains__. Итерация и индексация по индексу работают:
>>> events[0]
3
>>> for e in events:
... print(e)
Типичное применение: хранение нескольких последних показаний датчика для обнаружения изменений тенденции:
history = deque((), 10)
def push(reading):
history.append(reading)
if len(history) == 10 and history[-1] > 2 * history[0]:
print('reading is climbing')
2.37.3. OrderedDict – когда порядок является частью равенства¶
Обычный dict сохраняет порядок вставки начиная с MicroPython 1.13 и CPython 3.7. Это покрывает самую распространённую причину, по которой раньше обращались к collections.OrderedDict.
Что OrderedDict всё ещё даёт вам, чего не даёт обычный dict:
Сравнение
OrderedDictна равенство учитывает порядок. Два обычных словаря равны, когда у них одни и те же пары ключ/значение, независимо от порядка вставки. Два экземпляраOrderedDictравны только если их пары идут в одном и том же порядке:>>> from collections import OrderedDict >>> OrderedDict([('a', 1), ('b', 2)]) == OrderedDict([('b', 2), ('a', 1)]) False >>> {'a': 1, 'b': 2} == {'b': 2, 'a': 1} True
OrderedDictполезен, когда вы сериализуете конфигурацию в формат, который действительно заботится о порядке ключей (TOML, некоторые потребители YAML), или когда вы хотите дать чёткую подсказку в документации, что порядок имеет значение для читателей вашего кода.
Для повседневного кода предпочитайте встроенный dict. Обращайтесь к OrderedDict только когда семантика «порядок является частью равенства» или документирующая ценность действительно что-то даёт.
Каждый из трёх контейнеров имеет одно узкое применение. Именованные кортежи заменяют самодельные классы-записи; двусторонние очереди заменяют списки для ограниченных очередей; упорядоченные словари делают порядок вставки частью контракта.