2.37. Krotki nazwane i kolejki dwustronne¶
Listy, krotki, słowniki i zbiory pokrywają większość potrzeb dotyczących danych. Trzy inne kontenery w module collections pasują do specyficznych problemów, które wbudowane typy obsługują niezgrabnie.
2.37.1. namedtuple – typowane rekordy bez klasy¶
Zwykła krotka przechowuje wartości według pozycji. To jest w porządku dla małej, krótko żyjącej 2- lub 3-elementowej krotki, ale powyżej tego point[0] i point[1] zaczynają kłamać o tym, co przechowują. collections.namedtuple() zwraca nową podklasę krotki, której pola mają nazwy:
>>> 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
Argument fields to sekwencja łańcuchów z nazwami (lub jeden łańcuch rozdzielany białymi znakami w CPythonie; MicroPython jest bardziej rygorystyczny – przekaż krotkę lub listę).
Dlaczego używać namedtuple zamiast klasy?
To krotka. Iteracja, rozpakowywanie, równość, haszowanie i użycie jako klucz słownika – wszystko działa za darmo.
Jest niezmienna. Ponowne przypisanie
r.temp = ...zgłaszaAttributeError, co jest dokładnie tym, czego chcesz dla typu rekordu.Kosztuje mniej RAM niż instancja klasy z tymi samymi polami – pamięć krotki jest ciągła, bez
__dict__.
W porównaniu z równoważną klasą, deklaracja namedtuple to jedna linia. Kompromisem jest to, że pola są tylko do odczytu – aby „zmienić” odczyt, tworzysz nowy.
2.37.2. deque – ograniczony bufor cykliczny¶
Lista jest szybka na końcu (append / pop) i wolna na początku (insert(0, ...) / pop(0) – oba przesuwają każdy inny element). collections.deque jest szybka na obu końcach – to bufor cykliczny indeksowany wskaźnikami głowy i ogona, więc dodawanie i zdejmowanie po dowolnej stronie wykonuje tę samą stałą ilość pracy niezależnie od tego, ile elementów przechowuje deque.
Konstrukcja w MicroPythonie wymaga zarówno początkowego obiektu iterowalnego, jak i maksymalnej długości, w tej kolejności:
>>> from collections import deque
>>> events = deque((), 5)
>>> for i in range(8):
... events.append(i)
>>> list(events)
[3, 4, 5, 6, 7]
Gdy ograniczona deque jest pełna, każde append usuwa najstarszy element. To czyni deque idealną do „ostatnich N próbek”, „ostatnich N linii dziennika” lub dowolnego przesuwnego okna, które nie musi rosnąć w nieskończoność.
Metody udostępniane przez deque w MicroPythonie są celowo minimalne:
append(x)– dodaje na prawo.appendleft(x)– dodaje na lewo.extend(iterable)– dołącza każdy element z obiektu iterowalnego.pop()– usuwa i zwraca prawy koniec. ZgłaszaIndexErrorprzy pustej kolejce.popleft()– usuwa i zwraca lewy koniec.
Godne uwagi braki względem deque z CPythona: brak clear, count, index, remove, reverse, rotate, atrybutu maxlen oraz __contains__. Iteracja i indeksowanie przez subskrypt działają:
>>> events[0]
3
>>> for e in events:
... print(e)
Typowe zastosowanie: przechowywanie kilku ostatnich odczytów sensorów w celu wykrywania zmian trendu:
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 – gdy kolejność jest częścią równości¶
Zwykły dict zachowuje kolejność wstawiania od czasu MicroPythona 1.13 i CPythona 3.7. To pokrywa najczęstszy powód, dla którego ludzie sięgali wcześniej po collections.OrderedDict.
To, co OrderedDict nadal daje, a czego zwykły dict nie:
Równość
OrderedDictuwzględnia kolejność. Dwa zwykłe słowniki porównują się jako równe zawsze, gdy mają te same pary klucz/wartość, niezależnie od kolejności wstawiania. Dwie instancjeOrderedDictsą równe tylko wtedy, gdy ich pary są w tej samej kolejności:>>> from collections import OrderedDict >>> OrderedDict([('a', 1), ('b', 2)]) == OrderedDict([('b', 2), ('a', 1)]) False >>> {'a': 1, 'b': 2} == {'b': 2, 'a': 1} True
OrderedDictjest przydatny, gdy serializujesz konfigurację do formatu, który faktycznie dba o kolejność kluczy (TOML, niektórzy konsumenci YAML), lub gdy chcesz dać czytelną podpowiedź w dokumentacji, że kolejność ma znaczenie dla czytelników twojego kodu.
W codziennym kodzie preferuj wbudowany dict. Sięgaj po OrderedDict tylko wtedy, gdy semantyka kolejność-jako-część-równości lub wartość dokumentacyjna faktycznie coś daje.
Każdy z tych trzech kontenerów ma jedno wąskie zastosowanie. Krotki nazwane zastępują ręcznie pisane klasy rekordów; deque zastępują listy dla ograniczonych kolejek; uporządkowane słowniki czynią kolejność wstawiania częścią kontraktu.