W3docs

Módulo collections do Python

Aprenda o módulo collections do Python: Counter, defaultdict, namedtuple, deque, OrderedDict e ChainMap — com exemplos práticos e quando usar cada um.

O módulo collections embutido do Python fornece tipos de contêiner especializados que estendem ou substituem as estruturas padrão list, dict e tuple. Cada tipo resolve um problema específico que, de outra forma, exigiria várias linhas extras de código manual.

Este capítulo abrange os seis tipos mais utilizados: Counter, defaultdict, namedtuple, deque, OrderedDict e ChainMap. Para cada um, você verá qual problema ele resolve, como criá-lo e usá-lo, e os pontos de atenção a observar.

Nenhuma instalação é necessária — collections vem incluído em toda instalação do Python 3:

from collections import Counter, defaultdict, namedtuple, deque, OrderedDict, ChainMap

Counter

Counter é uma subclasse de dict projetada para contar objetos hasheáveis. Você fornece um iterável (ou uma string, ou argumentos nomeados) e ele retorna um objeto semelhante a um dicionário onde as chaves são os elementos e os valores são suas contagens.

Criando um Counter

from collections import Counter

# From a list
word_list = ['apple', 'banana', 'apple', 'cherry', 'banana', 'apple']
c = Counter(word_list)
print(c)
# Output: Counter({'apple': 3, 'banana': 2, 'cherry': 1})

Chaves ausentes retornam 0 em vez de lançar KeyError:

print(c['apple'])   # 3
print(c['mango'])   # 0  — no KeyError

Elementos mais comuns

most_common(n) retorna os n elementos com maior contagem como uma lista de tuplas (elemento, contagem), ordenados do mais ao menos frequente:

print(c.most_common(2))
# Output: [('apple', 3), ('banana', 2)]

Omita n para obter todos os elementos ordenados por frequência.

Aritmética de Counter

Counters suportam adição, subtração, interseção e união:

a = Counter(['a', 'a', 'b'])          # Counter({'a': 2, 'b': 1})
b = Counter(['a', 'b', 'b', 'c'])     # Counter({'b': 2, 'a': 1, 'c': 1})

print(a + b)   # Counter({'a': 3, 'b': 3, 'c': 1})
print(a - b)   # Counter({'a': 1})        — only positive counts kept
print(a & b)   # Counter({'a': 1, 'b': 1}) — minimum of each count
print(a | b)   # Counter({'a': 2, 'b': 2, 'c': 1}) — maximum of each count

Quando usar Counter: contagem de votos, frequência de palavras, contagem de caracteres, construção de histogramas.

Armadilha do Counter: subtração mantém apenas positivos

a - b descarta silenciosamente os elementos cujo resultado seria zero ou negativo. Se você precisa manter contagens negativas, use subtract() em vez disso:

a = Counter({'x': 2})
b = Counter({'x': 5})
a.subtract(b)
print(a)   # Counter({'x': -3})  — negative count preserved

defaultdict

defaultdict é uma subclasse de dict que chama uma função de fábrica para fornecer um valor padrão sempre que você acessa uma chave que ainda não existe. Isso elimina a necessidade de cláusulas de guarda if key not in d:.

Criando um defaultdict

Passe a fábrica como primeiro argumento:

from collections import defaultdict

dd = defaultdict(int)   # default value: int() == 0
words = ['cat', 'dog', 'cat', 'bird', 'dog', 'cat']
for word in words:
    dd[word] += 1       # no KeyError on first access

print(dict(dd))
# Output: {'cat': 3, 'dog': 2, 'bird': 1}

Sem defaultdict, você precisaria usar dd[word] = dd.get(word, 0) + 1 ou um Counter.

Agrupando itens com list como fábrica

groups = defaultdict(list)
data = [('fruit', 'apple'), ('veggie', 'carrot'), ('fruit', 'banana'), ('veggie', 'broccoli')]
for category, item in data:
    groups[category].append(item)

print(dict(groups))
# Output: {'fruit': ['apple', 'banana'], 'veggie': ['carrot', 'broccoli']}

Funções de fábrica comuns

FábricaValor padrãoUso típico
int0Contagem
float0.0Acumulação de somas
list[]Agrupamento de itens
setset()Coleta de valores únicos
str''Construção de strings
dict{}Mapeamentos aninhados

Você também pode passar uma lambda sem argumentos para um padrão personalizado: defaultdict(lambda: 'N/A').

Armadilha do defaultdict: acessar uma chave a cria

Ao contrário de dict.get(), um simples acesso dd[key] a uma chave inexistente insere essa chave com o valor padrão. Isso pode surpreender ao iterar ou verificar a existência de membros:

dd = defaultdict(int)
print('foo' in dd)   # False — key does not exist yet
_ = dd['foo']        # access inserts the key
print('foo' in dd)   # True  — key was silently created

Use dd.get('foo') ou 'foo' in dd quando quiser verificar sem efeitos colaterais.

Quando usar defaultdict: agrupamento de dados, construção de listas de adjacência para grafos, qualquer padrão em que você inicializa e depois atualiza.


namedtuple

namedtuple cria uma nova classe cujas instâncias são como tuplas comuns, mas com campos nomeados. O resultado é imutável, eficiente em memória (sem __dict__ por instância) e autodocumentado.

Criando um namedtuple

from collections import namedtuple

Point = namedtuple('Point', ['x', 'y'])
p = Point(3, 7)

print(p)         # Point(x=3, y=7)
print(p.x)       # 3
print(p.y)       # 7
print(p[0])      # 3  — index access still works

O primeiro argumento de namedtuple() é o nome do tipo (usado no repr). O segundo argumento é uma lista de nomes de campos (ou uma string separada por espaço/vírgula: 'x y').

Exemplo prático

Employee = namedtuple('Employee', ['name', 'department', 'salary'])
emp = Employee('Alice', 'Engineering', 95000)
print(emp.name, emp.department, emp.salary)
# Output: Alice Engineering 95000

O acesso por nome (emp.name) é muito mais claro do que o acesso posicional (row[0]) ao ler dados de arquivos CSV ou linhas de banco de dados.

Métodos úteis do namedtuple

# Convert to an ordered dictionary
print(p._asdict())        # {'x': 3, 'y': 7}

# Create a modified copy (namedtuples are immutable)
p2 = p._replace(x=10)
print(p2)                 # Point(x=10, y=7)
print(p)                  # Point(x=3, y=7)  — original unchanged

namedtuple vs dataclass

O Python 3.7 introduziu dataclasses.dataclass como alternativa. Escolha namedtuple quando quiser imutabilidade e compatibilidade total com tuplas (desempacotamento, indexação, hashing). Escolha dataclass quando precisar de campos mutáveis, fábricas de padrões ou métodos.

Quando usar namedtuple: representar registros (linhas de banco de dados, linhas de CSV, pares de coordenadas, cores RGB) onde imutabilidade e baixo uso de memória importam.


deque

deque (fila de dupla extremidade, pronunciado "dek") é uma sequência otimizada para adições e remoções O(1) em ambas as extremidades. Uma lista comum alcança O(1) com append e O(n) com insert(0, …); deque alcança O(1) em ambas as extremidades.

Criando um deque

from collections import deque

d = deque([1, 2, 3])
print(d)   # deque([1, 2, 3])

Adicionando e removendo elementos

d.append(4)        # add to right
d.appendleft(0)    # add to left
print(d)           # deque([0, 1, 2, 3, 4])

d.pop()            # remove from right  → 4
d.popleft()        # remove from left   → 0
print(d)           # deque([1, 2, 3])

Rotacionando um deque

rotate(n) desloca os elementos para a direita em n posições (negativo = esquerda):

d = deque([1, 2, 3])
d.rotate(1)
print(d)   # deque([3, 1, 2])

d.rotate(-1)
print(d)   # deque([1, 2, 3])

deque com limite (janela deslizante / buffer FIFO)

Definir maxlen limita o tamanho do deque. Quando novos itens são adicionados além do limite, itens caem automaticamente da extremidade oposta — perfeito para manter os últimos N eventos:

buffer = deque(maxlen=3)
for i in range(5):
    buffer.append(i)
print(buffer)   # deque([2, 3, 4], maxlen=3)

Armadilha do deque: acesso aleatório lento O(n)

deque não suporta acesso aleatório eficiente. d[500] é O(n), não O(1) como em uma lista. Se você frequentemente indexa por posição, use uma lista. Use deque apenas quando precisar de adições e remoções rápidas em ambas as extremidades.

Quando usar deque: implementar filas e pilhas, algoritmos de janela deslizante, busca em largura, armazenar as últimas N entradas de log.


OrderedDict

Desde o Python 3.7, o dict comum mantém a ordem de inserção. Então por que usar OrderedDict?

Dois motivos ainda são relevantes:

  1. move_to_end() — permite reordenar chaves eficientemente para o início ou o fim.
  2. Igualdade — duas instâncias de OrderedDict com as mesmas chaves mas em ordens de inserção diferentes são comparadas como não iguais, ao contrário de dicts comuns.

Criando e reordenando um OrderedDict

from collections import OrderedDict

od = OrderedDict()
od['one'] = 1
od['two'] = 2
od['three'] = 3
print(list(od.keys()))   # ['one', 'two', 'three']

od.move_to_end('one')          # move 'one' to the end
print(list(od.keys()))         # ['two', 'three', 'one']

od.move_to_end('three', last=False)  # move 'three' to the front
print(list(od.keys()))               # ['three', 'two', 'one']

Igualdade sensível à ordem

od1 = OrderedDict([('a', 1), ('b', 2)])
od2 = OrderedDict([('b', 2), ('a', 1)])
print(od1 == od2)   # False — different order

d1 = {'a': 1, 'b': 2}
d2 = {'b': 2, 'a': 1}
print(d1 == d2)     # True  — regular dicts ignore order

Quando usar OrderedDict: implementações de cache LRU (mover a chave usada recentemente para o fim), qualquer algoritmo onde a ordem de inserção deve fazer parte da igualdade.


ChainMap

ChainMap agrupa vários dicionários em uma única visão lógica. As buscas percorrem os mapas em ordem; escritas e exclusões afetam apenas o primeiro mapa.

Uso básico

from collections import ChainMap

defaults = {'color': 'blue', 'size': 'medium', 'theme': 'light'}
overrides = {'color': 'red', 'size': 'large'}

combined = ChainMap(overrides, defaults)
print(combined['color'])   # 'red'   — found in overrides first
print(combined['theme'])   # 'light' — not in overrides, falls back to defaults

As escritas vão apenas para o primeiro mapa:

combined['font'] = 'serif'
print(overrides)   # {'color': 'red', 'size': 'large', 'font': 'serif'}
print(defaults)    # {'color': 'blue', 'size': 'medium', 'theme': 'light'} — unchanged

Simulando escopos de variáveis com new_child()

base = ChainMap({'x': 1})
child = base.new_child({'x': 99, 'y': 2})
print(child['x'])            # 99  — child scope shadows parent
print(child['y'])            # 2
print(child.parents['x'])    # 1   — access parent scope directly

new_child() retorna um novo ChainMap com um dict vazio adicionado no início, que é como as próprias regras de escopo do Python (local → envolvente → global → embutido) são modeladas internamente.

Quando usar ChainMap: camadas de configuração (substituições do usuário → padrões do projeto → padrões globais), implementação de ambientes com escopo, combinação de argumentos de linha de comando com variáveis de ambiente e arquivos de configuração.


Escolhendo o Tipo Certo

Você precisa…Use
Contar ocorrências de itensCounter
Evitar KeyError com um valor padrãodefaultdict
Representar um registro com campos nomeadosnamedtuple
Adições/remoções rápidas em ambas as extremidades, ou um buffer delimitadodeque
Igualdade de dict sensível à ordem, ou move_to_end()OrderedDict
Mesclar vários dicts em uma visão sem copiarChainMap

Para mais informações sobre os tipos base que esses estendem, veja Python Dictionaries, Python Lists e Python Tuples. Para auxiliares baseados em iteradores na biblioteca padrão, veja o Módulo itertools do Python.

Prática

Prática
Which collections type returns 0 (instead of raising KeyError) when you access a missing key and count occurrences automatically?
Which collections type returns 0 (instead of raising KeyError) when you access a missing key and count occurrences automatically?
Prática
A deque with maxlen=3 already holds [1, 2, 3]. What does it contain after append(4) is called?
A deque with maxlen=3 already holds [1, 2, 3]. What does it contain after append(4) is called?
Prática
Which statement about OrderedDict is true in Python 3.7 and later?
Which statement about OrderedDict is true in Python 3.7 and later?
Was this page helpful?