2.37. Named tuples e deques¶
Listas, tuplos, dicionários e conjuntos cobrem a maioria das necessidades de dados. Três outros contentores no módulo collections servem problemas específicos que os tipos integrados tratam de forma desajeitada.
2.37.1. namedtuple – registos tipados sem uma classe¶
Um tuplo simples armazena valores por posição. Isso é aceitável para um 2- ou 3-tuplo pequeno e de curta duração, mas além disso point[0] e point[1] começam a ser pouco expressivos sobre o que contêm. collections.namedtuple() devolve uma nova subclasse de tuplo cujos campos têm nomes:
>>> 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
O argumento fields é uma sequência de strings de nomes (ou uma única string separada por espaços no CPython; o MicroPython é mais restritivo – passe um tuplo ou lista).
Porquê usar um namedtuple em vez de uma classe?
É um tuplo. Iteração, desempacotamento, igualdade, hashing e uso como chave de dicionário funcionam sem custo adicional.
É imutável. Reatribuir
r.temp = ...levantaAttributeError, o que é exatamente o que se quer num tipo de registo.Consome menos RAM do que uma instância de classe com os mesmos campos – o armazenamento do tuplo é contíguo, sem
__dict__.
Comparado com a classe equivalente, uma declaração de namedtuple é uma linha. A contrapartida é que os campos são apenas de leitura – para «alterar» uma leitura, cria-se uma nova.
2.37.2. deque – um ring buffer delimitado¶
Uma lista é rápida no fim (append / pop) e lenta no início (insert(0, ...) / pop(0) deslocam todos os outros elementos). Um collections.deque é rápido em ambas as extremidades – é um ring buffer indexado por ponteiros de cabeça e cauda, pelo que append e pop em qualquer extremidade executam na mesma quantidade fixa de trabalho independentemente do número de itens que a deque contém.
A construção em MicroPython requer tanto um iterável inicial como um comprimento máximo, nessa ordem:
>>> from collections import deque
>>> events = deque((), 5)
>>> for i in range(8):
... events.append(i)
>>> list(events)
[3, 4, 5, 6, 7]
Quando uma deque delimitada está cheia, cada append descarta o item mais antigo. Isso torna a deque ideal para «as últimas N amostras», «as últimas N linhas de registo», ou qualquer janela deslizante que não precise de crescer indefinidamente.
Os métodos expostos na deque do MicroPython são deliberadamente mínimos:
append(x)– adicionar à direita.appendleft(x)– adicionar à esquerda.extend(iterable)– acrescentar cada item do iterável.pop()– remover e devolver o elemento da extremidade direita. LevantaIndexErrorse estiver vazia.popleft()– remover e devolver o elemento da extremidade esquerda.
Omissões notáveis em relação à deque do CPython: sem clear, count, index, remove, reverse, rotate, atributo maxlen, nem __contains__. Iteração e indexação por subscrição funcionam:
>>> events[0]
3
>>> for e in events:
... print(e)
Uma utilização típica: manter as últimas leituras do sensor para detetar mudanças de tendência:
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 – quando a ordem faz parte da igualdade¶
O dict regular preserva a ordem de inserção desde o MicroPython 1.13 e o CPython 3.7. Isso cobre a razão mais comum pela qual as pessoas recorriam ao collections.OrderedDict.
O que o OrderedDict ainda oferece que um dict simples não oferece:
A igualdade do
OrderedDictconsidera a ordem. Dois dicionários regulares comparam-se como iguais sempre que têm os mesmos pares chave/valor, independentemente da ordem de inserção. Duas instâncias deOrderedDictsão iguais apenas se os seus pares estiverem na mesma ordem:>>> from collections import OrderedDict >>> OrderedDict([('a', 1), ('b', 2)]) == OrderedDict([('b', 2), ('a', 1)]) False >>> {'a': 1, 'b': 2} == {'b': 2, 'a': 1} True
O
OrderedDicté útil quando se está a serializar configuração para um formato que se preocupa com a ordem das chaves (TOML, alguns consumidores YAML), ou quando se quer uma sugestão de documentação clara de que a ordem é importante para os leitores do código.
Para código do dia-a-dia, prefira o dict integrado. Recorra ao OrderedDict apenas quando a semântica de ordem-como-parte-da-igualdade ou o valor documental realmente acrescentam algo.
Os três contentores têm cada um um enquadramento específico. Os named tuples substituem classes de registo feitas à mão; as deques substituem listas para filas delimitadas; os dicionários ordenados tornam a ordem de inserção parte do contrato.