2.37. Named tuples and deques¶
Lists, tuples, dictionaries, and sets cover most data needs. Three
other containers in the collections module fit specific
problems that the built-ins handle clumsily.
2.37.1. namedtuple – typed records without a class¶
A plain tuple stores values by position. That’s fine for a small,
short-lived 2- or 3-tuple, but past that point[0] and
point[1] start to lie about what they hold. collections.namedtuple()
returns a new tuple subclass whose fields have names:
>>> 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
The fields argument is a sequence of name strings (or one whitespace- separated string in CPython; MicroPython is stricter – pass a tuple or list).
Why use a namedtuple instead of a class?
It’s a tuple. Iteration, unpacking, equality, hashing, and use as a dict key all work for free.
It’s immutable. Reassigning
r.temp = ...raisesAttributeError, which is exactly what you want for a record type.It costs less RAM than a class instance with the same fields – the tuple’s storage is contiguous, with no
__dict__.
Compared with the equivalent class, a namedtuple declaration is
one line. The trade-off is that fields are read-only – to “change”
a reading you make a new one.
2.37.2. deque – a bounded ring buffer¶
A list is fast at the end (append / pop) and slow at the
start (insert(0, ...) / pop(0) both shift every other
element). A collections.deque is fast at both ends – it’s
a ring buffer indexed by head and tail pointers, so append and pop
on either side run in the same fixed amount of work regardless of
how many items the deque holds.
Construction in MicroPython requires both an initial iterable and a max length, in that order:
>>> from collections import deque
>>> events = deque((), 5)
>>> for i in range(8):
... events.append(i)
>>> list(events)
[3, 4, 5, 6, 7]
When a bounded deque is full, every append drops the oldest item.
That makes the deque ideal for “the last N samples”, “the last N log
lines”, or any rolling window that doesn’t need to grow forever.
The methods exposed on MicroPython’s deque are deliberately minimal:
append(x)– add to the right.appendleft(x)– add to the left.extend(iterable)– append each item from the iterable.pop()– remove and return the right end. RaisesIndexErroron empty.popleft()– remove and return the left end.
Notable omissions from CPython’s deque: no clear, count,
index, remove, reverse, rotate, maxlen attribute,
or __contains__. Iteration and subscript indexing work:
>>> events[0]
3
>>> for e in events:
... print(e)
A typical use: keeping the last few sensor readings to detect trend changes:
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 – when order is part of equality¶
Regular dict has preserved insertion order since
MicroPython 1.13 and CPython 3.7. That covers the most common reason
people used to reach for collections.OrderedDict.
What OrderedDict still gives you that a plain dict does
not:
OrderedDictequality considers order. Two regular dicts compare equal whenever they have the same key/value pairs, regardless of insertion order. TwoOrderedDictinstances are equal only if their pairs are in the same order:>>> from collections import OrderedDict >>> OrderedDict([('a', 1), ('b', 2)]) == OrderedDict([('b', 2), ('a', 1)]) False >>> {'a': 1, 'b': 2} == {'b': 2, 'a': 1} True
OrderedDictis useful when you’re serialising config to a format that does care about key order (TOML, some YAML consumers), or when you want a clear documentation hint that order matters for the readers of your code.
For everyday code, prefer the built-in dict. Reach for
OrderedDict only when the order-is-part-of-equality semantics or
the documentary value actually buys something.
The three containers each have one narrow fit. Named tuples replace hand-rolled record classes; deques replace lists for bounded queues; ordered dicts make insertion order part of the contract.