Escolhendo a estrutura certa
Esta aula ensina a escolher a estrutura de dados ideal em Python (listas, tuplas, sets e dicionários) com base em características, complexidade, casos de uso e performance. O aluno aprenderá a analisar requisitos e tomar decisões informadas para escrever código eficiente.
Em Python, a escolha da estrutura de dados correta é fundamental para escrever código eficiente, legível e de fácil manutenção. Cada estrutura — listas, tuplas, sets e dicionários — possui características únicas que as tornam mais adequadas para determinados cenários. Nesta aula, vamos explorar as diferenças entre elas, analisar a complexidade de tempo e espaço de suas operações, discutir casos de uso típicos e comparar a performance. Ao final, você será capaz de selecionar a estrutura mais apropriada para cada problema.
Lista vs tupla vs set vs dict
Vamos revisar rapidamente as características fundamentais de cada estrutura:
- Lista: sequência mutável, ordenada, indexada. Permite elementos duplicados. Sintaxe:
[1, 2, 3]. - Tupla: sequência imutável, ordenada, indexada. Permite elementos duplicados. Sintaxe:
(1, 2, 3). - Set: coleção não ordenada, mutável, sem índices. Não permite duplicatas. Sintaxe:
{1, 2, 3}. - Dict: mapeamento chave-valor, mutável, ordenado (a partir do Python 3.7). Chaves únicas, valores podem ser duplicados. Sintaxe:
{'a': 1, 'b': 2}.
A escolha entre elas depende de fatores como necessidade de ordem, mutabilidade, unicidade e tipo de acesso (por índice, chave ou valor).
Complexidade
A complexidade de tempo (Big O) das operações comuns varia entre as estruturas. Conhecê-la ajuda a prever o desempenho em diferentes cenários.
| Operação | Lista | Tupla | Set | Dict |
|---|---|---|---|---|
| Acesso por índice/chave | O(1) | O(1) | N/A | O(1) médio |
| Inserção no final | O(1) amortizado | N/A (imutável) | O(1) médio | O(1) médio |
| Inserção no início | O(n) | N/A | N/A | N/A |
| Remoção por valor | O(n) | N/A | O(1) médio | O(1) médio (por chave) |
| Busca por valor | O(n) | O(n) | O(1) médio | O(1) médio (chave) |
| Ordenação | O(n log n) | O(n log n) via sorted() | N/A (não ordenado) | N/A (ordenação por chave via sorted()) |
Note que sets e dicts usam tabelas hash, proporcionando buscas rápidas, mas sem ordem garantida (embora dicts mantenham ordem de inserção desde Python 3.7). Listas e tuplas são sequências lineares, com acesso indexado rápido, mas buscas lentas.
Casos de uso
Cada estrutura se destaca em cenários específicos:
- Lista: quando a ordem importa, você precisa de mutabilidade, ou deseja armazenar itens duplicados. Exemplos: fila de tarefas, histórico de ações, coleção de resultados.
- Tupla: quando a imutabilidade é desejada (proteção contra alterações acidentais), ou para representar registros leves (ex.: coordenadas (x, y)). Também usada como chave de dicionário (por ser hashable).
- Set: quando a unicidade é essencial e a ordem não importa. Ideal para testes de pertinência, remoção de duplicatas, operações de conjunto (união, interseção). Exemplo: conjunto de IDs de usuários únicos.
- Dict: quando você precisa associar chaves a valores, como em um banco de dados em memória. Exemplos: contagem de frequências, cache, mapeamento de nomes para objetos.
Exemplo prático: suponha que você precise armazenar as notas dos alunos. Se cada aluno tem uma nota, um dict com o nome como chave e a nota como valor é ideal. Se você precisa manter a ordem de chegada, use uma lista de tuplas (nome, nota). Para garantir que não haja notas duplicadas (improvável), um set não faria sentido.
Performance
A performance prática depende do tamanho dos dados e das operações mais frequentes. Vamos comparar cenários comuns com código:
import timeit
# Cenário 1: Busca por valor em lista vs set
dados_lista = list(range(100_000))
dados_set = set(range(100_000))
tempo_lista = timeit.timeit(lambda: 99_999 in dados_lista, number=1000)
tempo_set = timeit.timeit(lambda: 99_999 in dados_set, number=1000)
print(f"Busca em lista: {tempo_lista:.5f}s")
print(f"Busca em set: {tempo_set:.5f}s")
# Resultado típico: set é muito mais rápido (O(1) vs O(n))
# Cenário 2: Inserção em lista vs set
tempo_lista_ins = timeit.timeit(lambda: dados_lista.append(100_000), number=1000)
tempo_set_ins = timeit.timeit(lambda: dados_set.add(100_000), number=1000)
print(f"Inserção em lista: {tempo_lista_ins:.5f}s")
print(f"Inserção em set: {tempo_set_ins:.5f}s")
# Ambos são rápidos, mas set pode ser ligeiramente mais lento devido ao hash
Em geral, use sets e dicts para buscas rápidas e remoção de duplicatas; listas para sequências ordenadas e mutáveis; tuplas para dados imutáveis e leves. A escolha errada pode levar a código lento: por exemplo, usar uma lista para verificar pertinência em um grande conjunto de dados é ineficiente.
Boas práticas
- Prefira tuplas a listas quando os dados não precisam ser modificados — isso torna o código mais seguro e pode melhorar a performance.
- Use sets para remover duplicatas rapidamente:
list(set(minha_lista)). - Ao iterar sobre um dict, lembre-se de que a ordem de inserção é preservada (Python 3.7+).
- Evite usar listas como chaves de dicionário; use tuplas se precisar de uma chave composta.
- Para grandes volumes de dados, considere a complexidade de memória: listas e tuplas são mais compactas que sets e dicts, que têm overhead de tabela hash.
Referências
- Documentação oficial: Estruturas de dados
- Wiki Python: Complexidade de tempo
- Real Python: Listas e tuplas
- Real Python: Sets em Python
- Real Python: Dicionários em Python
Exercícios
- Explique por que usar uma lista para verificar se um elemento existe é menos eficiente do que usar um set, para grandes volumes de dados.
✓ Resposta: A lista realiza uma busca linear O(n) percorrendo todos os elementos, enquanto o set usa uma tabela hash que permite busca O(1) médio. Para grandes volumes, a diferença é drástica.
- Dado o seguinte cenário: você precisa armazenar as coordenadas (x, y) de pontos em um plano cartesiano, e esses pontos nunca serão modificados. Qual estrutura é mais adequada? Justifique.
✓ Resposta: Tupla. Como as coordenadas são imutáveis, a tupla é ideal: é hashable (pode ser usada como chave de dict, se necessário), mais leve que uma lista e protege contra alterações acidentais.
- Escreva um código que remova elementos duplicados de uma lista preservando a ordem original. Use uma estrutura adequada.
✓ Resposta:
def remove_duplicatas_ordenado(lista): vistos = set() resultado = [] for item in lista: if item not in vistos: vistos.add(item) resultado.append(item) return resultado # Exemplo: print(remove_duplicatas_ordenado([3, 1, 2, 1, 3, 4])) # [3, 1, 2, 4] - Qual é a complexidade de tempo para acessar um elemento pelo índice em uma lista e em uma tupla? E para acessar um valor por chave em um dict?
✓ Resposta: Tanto lista quanto tupla têm acesso O(1) por índice, pois são arrays contíguos. Dict tem acesso O(1) médio por chave devido à tabela hash.
- Crie um dicionário que mapeie nomes de alunos a suas notas. Em seguida, encontre o aluno com a maior nota. Use apenas estruturas nativas.
✓ Resposta:
notas = {'Ana': 8.5, 'Bruno': 9.0, 'Carla': 7.5} melhor_aluno = max(notas, key=notas.get) print(melhor_aluno, notas[melhor_aluno]) # Bruno 9.0