2.37. Named tuples en deques¶
Lijsten, tuples, dictionaries en sets dekken de meeste databehoeften af. Drie andere containers in de collections-module passen bij specifieke problemen die de ingebouwde types onhandig afhandelen.
2.37.1. namedtuple – getypeerde records zonder een klasse¶
Een gewone tuple slaat waarden op per positie. Dat is prima voor een kleine, kortlevende 2- of 3-tuple, maar daarna beginnen point[0] en point[1] te liegen over wat ze bevatten. collections.namedtuple() retourneert een nieuwe tuple-subklasse waarvan de velden namen hebben:
>>> 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
Het velden-argument is een reeks van naamstrings (of één string gescheiden door witruimte in CPython; MicroPython is strenger – geef een tuple of lijst door).
Waarom een namedtuple gebruiken in plaats van een klasse?
Het is een tuple. Iteratie, uitpakken, gelijkheid, hashing en gebruik als dict-sleutel werken allemaal gratis.
Het is onveranderlijk. Het opnieuw toewijzen van
r.temp = ...veroorzaakt eenAttributeError, wat precies is wat je wilt voor een recordtype.Het kost minder RAM dan een klasse-instantie met dezelfde velden – de opslag van de tuple is aaneengesloten, zonder
__dict__.
Vergeleken met de equivalente klasse is een namedtuple-declaratie één regel. De afweging is dat velden alleen-lezen zijn – om een meting te “wijzigen” maak je een nieuwe.
2.37.2. deque – een begrensde ringbuffer¶
Een lijst is snel aan het einde (append / pop) en traag aan het begin (insert(0, ...) / pop(0) verschuiven beide elk ander element). Een collections.deque is snel aan beide uiteinden – het is een ringbuffer geïndexeerd door head- en tail-pointers, dus append en pop aan beide kanten kosten dezelfde vaste hoeveelheid werk, ongeacht hoeveel items de deque bevat.
Constructie in MicroPython vereist zowel een initiële iterable als een maximale lengte, in die volgorde:
>>> from collections import deque
>>> events = deque((), 5)
>>> for i in range(8):
... events.append(i)
>>> list(events)
[3, 4, 5, 6, 7]
Wanneer een begrensde deque vol is, laat elke append het oudste item vallen. Dat maakt de deque ideaal voor “de laatste N samples”, “de laatste N logregels”, of elk schuivend venster dat niet eindeloos hoeft te groeien.
De methoden die op MicroPython’s deque worden blootgesteld zijn bewust minimaal:
append(x)– toevoegen aan de rechterkant.appendleft(x)– toevoegen aan de linkerkant.extend(iterable)– voeg elk item uit de iterable toe.pop()– verwijder en retourneer het rechteruiteinde. VeroorzaaktIndexErrorbij leeg.popleft()– verwijder en retourneer het linkeruiteinde.
Opvallende weglatingen ten opzichte van CPython’s deque: geen clear, count, index, remove, reverse, rotate, maxlen-attribuut of __contains__. Iteratie en subscript-indexering werken:
>>> events[0]
3
>>> for e in events:
... print(e)
Een typisch gebruik: het bijhouden van de laatste paar sensormetingen om trendveranderingen te detecteren:
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 – wanneer volgorde deel uitmaakt van gelijkheid¶
Een gewone dict behoudt de invoegvolgorde sinds MicroPython 1.13 en CPython 3.7. Dat dekt de meest voorkomende reden waarom mensen vroeger naar collections.OrderedDict grepen.
Wat OrderedDict je nog steeds geeft dat een gewone dict niet doet:
OrderedDict-gelijkheid houdt rekening met volgorde. Twee gewone dicts zijn gelijk wanneer ze dezelfde sleutel/waarde-paren hebben, ongeacht de invoegvolgorde. TweeOrderedDict-instanties zijn alleen gelijk als hun paren in dezelfde volgorde staan:>>> 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 nuttig wanneer je configuratie serialiseert naar een formaat dat wel om sleutelvolgorde geeft (TOML, sommige YAML-verbruikers), of wanneer je een duidelijke documentatiehint wilt dat volgorde van belang is voor de lezers van je code.
Geef voor alledaagse code de voorkeur aan de ingebouwde dict. Grijp alleen naar OrderedDict wanneer de semantiek waarbij volgorde deel uitmaakt van gelijkheid of de documentatiewaarde echt iets oplevert.
De drie containers hebben elk één smalle toepassing. Named tuples vervangen handgemaakte recordklassen; deques vervangen lijsten voor begrensde wachtrijen; ordered dicts maken invoegvolgorde onderdeel van het contract.