O módulo collections
Esta aula apresenta o módulo collections do Python, que oferece estruturas de dados especializadas e eficientes. Exploramos defaultdict, Counter, deque e namedtuple, com exemplos práticos de uso.
O módulo collections é uma das bibliotecas padrão mais úteis do Python. Ele fornece alternativas elegantes e eficientes para as estruturas de dados built-in, como dicionários, listas e tuplas. Nesta aula, vamos explorar quatro dessas estruturas: defaultdict, Counter, deque e namedtuple. Cada uma resolve problemas comuns de forma mais concisa e performática.
defaultdict
O defaultdict é uma subclasse de dict que fornece um valor padrão para chaves inexistentes. Isso elimina a necessidade de verificar se uma chave existe antes de acessá-la ou modificá-la, tornando o código mais limpo e evitando erros de KeyError.
Para usar, importe de collections e passe uma função de fábrica (callable) que retorna o valor padrão. Por exemplo, defaultdict(list) cria um dicionário onde valores ausentes são inicializados como listas vazias. Isso é útil para agrupar itens.
from collections import defaultdict
# Agrupar palavras por primeira letra
words = ['banana', 'abacaxi', 'abacate', 'beterraba', 'ameixa']
groups = defaultdict(list)
for word in words:
groups[word[0]].append(word)
print(groups)
# defaultdict(<class 'list'>, {'b': ['banana', 'beterraba'], 'a': ['abacaxi', 'abacate', 'ameixa']})
Counter
Counter é uma subclasse de dict usada para contar objetos hasháveis. Ele recebe um iterável e cria um dicionário onde as chaves são os elementos e os valores são suas contagens. É ideal para tarefas como contar frequências de palavras, caracteres, etc.
Oferece métodos úteis como most_common(n) que retorna os n elementos mais frequentes, e suporte para operações aritméticas entre contadores.
from collections import Counter
# Contar letras em uma string
texto = "paralelepipedo"
contagem = Counter(texto)
print(contagem)
# Counter({'p': 3, 'e': 3, 'a': 2, 'l': 2, 'r': 1, 'i': 1, 'd': 1, 'o': 1})
# Os 3 mais comuns
print(contagem.most_common(3))
# [('p', 3), ('e', 3), ('a', 2)]
deque
deque (double-ended queue) é uma lista otimizada para inserções e remoções rápidas em ambas as extremidades. Enquanto listas Python têm complexidade O(n) para inserir/remover no início, deque faz isso em O(1). É ideal para filas, pilhas e buffers circulares.
Principais métodos: append, appendleft, pop, popleft e rotate. Suporta também um tamanho máximo (maxlen), descartando automaticamente itens do lado oposto quando o limite é atingido.
from collections import deque
# Fila simples
fila = deque(['a', 'b', 'c'])
fila.append('d')
fila.popleft() # remove 'a'
print(fila) # deque(['b', 'c', 'd'])
# Buffer circular com maxlen
buffer = deque(maxlen=3)
for i in range(5):
buffer.append(i)
print(buffer)
# deque([0], maxlen=3)
# deque([0, 1], maxlen=3)
# deque([0, 1, 2], maxlen=3)
# deque([1, 2, 3], maxlen=3)
# deque([2, 3, 4], maxlen=3)
namedtuple
namedtuple é uma fábrica de tuplas com campos nomeados. Ela cria classes de tupla imutáveis que podem ser acessadas tanto por índice quanto por nome, tornando o código mais legível sem perder a eficiência da tupla.
Para criar, use namedtuple('Nome', 'campo1 campo2') ou uma lista de strings. Os objetos gerados são imutáveis e consomem a mesma memória de uma tupla.
from collections import namedtuple
# Definir um tipo Ponto
Ponto = namedtuple('Ponto', ['x', 'y'])
p = Ponto(10, 20)
print(p.x, p.y) # 10 20
print(p[0]) # 10 (acesso por índice)
# Útil para registros simples
Aluno = namedtuple('Aluno', 'nome matricula nota')
a = Aluno('João', '1234', 9.5)
print(f'{a.nome} - {a.nota}') # João - 9.5
Boas Práticas
- Use
defaultdictpara evitar verificações de existência em dicionários. - Prefira
Counterpara contagens, em vez de dicionários manuais. - Use
dequequando precisar de filas ou pilhas com alta performance nas extremidades. namedtupleé ideal para dados imutáveis e legíveis, como registros leves.
Referências
- Documentação oficial do módulo collections
- defaultdict - documentação
- Counter - documentação
- deque - documentação
- namedtuple - documentação
- Real Python: collections module
- GeeksforGeeks: Python collections
Exercícios
- Usando
defaultdict, escreva uma função que receba uma lista de números e retorne um dicionário onde as chaves são os números e os valores são listas de índices onde cada número aparece. - Dada uma string, use
Counterpara encontrar o caractere mais frequente (desconsiderando espaços). - Implemente uma fila usando
dequeque permita enfileirar e desenfileirar elementos, e também permita ver o primeiro elemento sem removê-lo. - Crie um tipo
namedtuplechamadoLivrocom campostitulo,autoreano. Instancie dois livros e crie uma lista com eles. - Escreva uma função que receba uma lista de palavras e retorne um dicionário com a contagem de palavras, mas ignorando diferenças entre maiúsculas/minúsculas. Use
Counter.
from collections import defaultdict
def indices_numeros(lista):
d = defaultdict(list)
for i, num in enumerate(lista):
d[num].append(i)
return d
print(indices_numeros([1, 2, 1, 3, 2, 1]))
# defaultdict(<class 'list'>, {1: [0, 2, 5], 2: [1, 4], 3: [3]})
from collections import Counter
def caractere_mais_frequente(texto):
texto = texto.replace(' ', '')
if not texto:
return None
contagem = Counter(texto)
return contagem.most_common(1)[0][0]
print(caractere_mais_frequente("hello world")) # 'l'
from collections import deque
class Fila:
def __init__(self):
self._dados = deque()
def enfileirar(self, item):
self._dados.append(item)
def desenfileirar(self):
if not self._dados:
raise IndexError("Fila vazia")
return self._dados.popleft()
def primeiro(self):
if not self._dados:
raise IndexError("Fila vazia")
return self._dados[0]
def __len__(self):
return len(self._dados)
f = Fila()
f.enfileirar(1)
f.enfileirar(2)
print(f.primeiro()) # 1
print(f.desenfileirar()) # 1
print(len(f)) # 1
from collections import namedtuple
Livro = namedtuple('Livro', ['titulo', 'autor', 'ano'])
livro1 = Livro('1984', 'George Orwell', 1949)
livro2 = Livro('Dom Casmurro', 'Machado de Assis', 1899)
livros = [livro1, livro2]
for livro in livros:
print(f'{livro.titulo} ({livro.ano}) - {livro.autor}')
# 1984 (1949) - George Orwell
# Dom Casmurro (1899) - Machado de Assis
from collections import Counter
def contar_palavras(lista):
# Converter todas para minúsculas
lista_lower = [palavra.lower() for palavra in lista]
return Counter(lista_lower)
print(contar_palavras(['Python', 'python', 'PYTHON', 'Java', 'java']))
# Counter({'python': 3, 'java': 2})