Comprehensions de Dicionário e Conjunto em Python
Domine comprehensions de dicionário, conjunto e expressões geradoras em Python com sintaxe clara, exemplos reais e armadilhas comuns.
Comprehensions de dicionário, de conjunto e expressões geradoras estendem a sintaxe compacta de list comprehension para outras estruturas de dados. Este capítulo explica cada forma em profundidade — sintaxe, filtragem, aninhamento, padrões do mundo real e quando evitá-las.
Comprehensions de Dicionário
Uma comprehension de dicionário constrói um novo dict a partir de qualquer iterável em uma única expressão. Em vez de escrever um loop for que chama d[key] = value em cada iteração, você descreve o mapeamento de forma concisa dentro de chaves.
Sintaxe
new_dict = {key_expr: value_expr for item in iterable}Adicione um filtro if opcional após o iterável:
new_dict = {key_expr: value_expr for item in iterable if condition}| Parte | Função |
|---|---|
key_expr | Expressão que produz cada chave |
value_expr | Expressão que produz cada valor |
item | Variável do loop — assume cada valor do iterable por vez |
if condition | Filtro opcional — ignora itens onde condition é False |
Exemplo Básico: Construindo um Mapa de Quadrados
squares = {x: x ** 2 for x in range(1, 6)}
print(squares)
# {1: 1, 2: 4, 3: 9, 4: 16, 5: 25}O loop for equivalente:
squares = {}
for x in range(1, 6):
squares[x] = x ** 2Ambos produzem o mesmo resultado; a forma de comprehension é mais concisa e expressa a intenção de relance.
Filtrando Entradas
Mantenha apenas os pares que satisfazem uma condição:
prices = {"apple": 1.20, "banana": 0.50, "orange": 0.80, "grape": 2.50}
expensive = {item: price for item, price in prices.items() if price >= 1.00}
print(expensive)
# {'apple': 1.2, 'grape': 2.5}Transformando Valores
Aplique uma função ou expressão aritmética a cada valor:
prices = {"apple": 1.20, "banana": 0.50, "orange": 0.80}
# Apply a 10% discount and round to 2 decimal places
discounted = {item: round(price * 0.9, 2) for item, price in prices.items()}
print(discounted)
# {'apple': 1.08, 'banana': 0.45, 'orange': 0.72}Trocando Chaves e Valores
Inverta um dicionário (funciona corretamente quando todos os valores são únicos):
capitals = {"France": "Paris", "Germany": "Berlin", "Japan": "Tokyo"}
inverted = {city: country for country, city in capitals.items()}
print(inverted)
# {'Paris': 'France', 'Berlin': 'Germany', 'Tokyo': 'Japan'}Se os valores não forem únicos, entradas posteriores sobrescrevem as anteriores. Use isso apenas quando tiver certeza de que os valores formam um mapeamento um-para-um.
Construindo a partir de Dois Iteráveis com zip()
zip() emparelha itens de duas sequências, facilitando a construção de um dicionário a partir de listas separadas de chaves e valores:
keys = ["name", "age", "city"]
values = ["Alice", 30, "Berlin"]
profile = {k: v for k, v in zip(keys, values)}
print(profile)
# {'name': 'Alice', 'age': 30, 'city': 'Berlin'}Isso equivale a dict(zip(keys, values)), mas a forma de comprehension facilita adicionar um filtro ou transformar valores ao mesmo tempo.
Normalizando Chaves
Uma tarefa comum do mundo real é limpar as chaves de um dicionário — por exemplo, converter todas as chaves para minúsculas ao mesclar dados de diferentes fontes:
raw = {"Name": "Alice", "AGE": 30, "City": "Berlin"}
normalised = {k.lower(): v for k, v in raw.items()}
print(normalised)
# {'name': 'Alice', 'age': 30, 'city': 'Berlin'}Expressão de Valor Condicional (Ternário no Valor)
Você pode usar uma expressão ternária na posição do valor para escolher entre dois valores por item:
scores = {"Alice": 95, "Bob": 62, "Carol": 78, "Dave": 45}
grades = {name: "pass" if score >= 70 else "fail" for name, score in scores.items()}
print(grades)
# {'Alice': 'pass', 'Bob': 'fail', 'Carol': 'pass', 'Dave': 'fail'}Comprehensions de Dicionário Aninhadas
Você pode aninhar uma comprehension para lidar com dados de múltiplos níveis. Por exemplo, extraindo um campo de uma lista de dicionários aninhados:
students = [
{"name": "Alice", "score": 95, "grade": "A"},
{"name": "Bob", "score": 82, "grade": "B"},
{"name": "Carol", "score": 91, "grade": "A"},
]
# Build a {name: score} mapping from the list
name_to_score = {s["name"]: s["score"] for s in students}
print(name_to_score)
# {'Alice': 95, 'Bob': 82, 'Carol': 91}Mantenha o aninhamento raso. Se a lógica se tornar difícil de acompanhar, extraia uma função auxiliar ou use um loop for simples.
Comprehensions de Conjunto
Uma comprehension de conjunto constrói um set em uma única expressão. Como os conjuntos contêm apenas elementos únicos, duplicatas são removidas automaticamente.
Sintaxe
new_set = {expression for item in iterable}
new_set = {expression for item in iterable if condition}A única diferença visual em relação a uma comprehension de dicionário é a ausência de dois-pontos — há uma expressão, não um par key: value.
Exemplo Básico: Quadrados Únicos
numbers = [1, 2, 2, 3, 3, 3, 4]
unique_squares = {x ** 2 for x in numbers}
print(unique_squares)
# {1, 4, 9, 16}A ordem de saída não é garantida — os conjuntos não são ordenados, portanto não confie em uma ordem de exibição específica.
Deduplicando ao Transformar
Um padrão comum é normalizar strings e deduplicar em um único passo:
tags = ["Python", "python", "PYTHON", "Data", "data", "DATA"]
unique_tags = {tag.lower() for tag in tags}
print(unique_tags)
# {'python', 'data'}Filtrando com uma Condição
words = ["cat", "elephant", "dog", "rhinoceros", "ant"]
long_words = {w for w in words if len(w) > 4}
print(long_words)
# {'elephant', 'rhinoceros'}Encontrando Elementos Comuns
Comprehensions de conjunto se combinam naturalmente com operações de conjunto:
list_a = [1, 2, 3, 4, 5, 5, 6]
list_b = [4, 5, 6, 7, 8]
set_a = {x for x in list_a}
set_b = {x for x in list_b}
common = set_a & set_b
print(common)
# {4, 5, 6}Para uma conversão direta sem transformação, set(list_a) é mais simples. Use uma comprehension de conjunto quando precisar filtrar ou transformar durante a conversão.
Expressões Geradoras
Uma expressão geradora parece uma list comprehension, mas usa parênteses em vez de colchetes. Em vez de construir toda a coleção na memória de uma vez, ela produz um valor por vez, sob demanda (de forma preguiçosa).
Sintaxe
gen = (expression for item in iterable)
gen = (expression for item in iterable if condition)Por Que Usar uma Expressão Geradora?
| Cenário | List comprehension | Expressão geradora |
|---|---|---|
| Precisa iterar o resultado várias vezes | Sim | Não — geradores são esgotados após uma passagem |
Precisa de acesso aleatório por índice (result[3]) | Sim | Não |
O resultado é passado para sum(), max(), any(), etc. | Funciona, mas aloca uma lista | Preferível — transmite valores sem construir uma lista |
| Sequência muito grande ou infinita | Pode ficar sem memória | Eficiente — um valor por vez |
Exemplo Básico
# List comprehension — builds the entire list in memory
squares_list = [x ** 2 for x in range(1, 6)]
print(squares_list) # [1, 4, 9, 16, 25]
# Generator expression — yields values one at a time
squares_gen = (x ** 2 for x in range(1, 6))
print(squares_gen) # <generator object <genexpr> at 0x...>
print(list(squares_gen)) # [1, 4, 9, 16, 25]O objeto gerador em si não é a lista — você o consome iterando-o ou passando-o para uma função.
Passando Diretamente para Funções Integradas
Quando você passa uma expressão geradora como único argumento para uma função, pode omitir o par extra de parênteses:
total = sum(x ** 2 for x in range(1, 1001))
print(total) # 333833500
maximum = max(len(word) for word in ["apple", "banana", "kiwi"])
print(maximum) # 6
any_negative = any(x < 0 for x in [1, -2, 3])
print(any_negative) # TrueVantagem de Memória
Para grandes volumes de dados, uma expressão geradora evita carregar tudo na RAM:
# Simulate a large log file as a list of strings
log_lines = [f"ERROR line {i}" if i % 100 == 0 else f"INFO line {i}" for i in range(1_000_000)]
# Generator scans lines without building an intermediate list
error_count = sum(1 for line in log_lines if line.startswith("ERROR"))
print(error_count) # 10000Geradores São Esgotados Após Uma Passagem
Esta é a armadilha mais comum:
gen = (x * 2 for x in range(5))
print(list(gen)) # [0, 2, 4, 6, 8]
print(list(gen)) # [] — the generator is now emptySe você precisar iterar o resultado mais de uma vez, use uma list comprehension ou recrie o gerador.
Encadeando Expressões Geradoras
Expressões geradoras podem ser compostas sem criar listas intermediárias. Cada estágio puxa valores do anterior:
numbers = range(1, 11)
# Stage 1: filter even numbers
evens = (x for x in numbers if x % 2 == 0)
# Stage 2: square them
even_squares = (x ** 2 for x in evens)
print(list(even_squares)) # [4, 16, 36, 64, 100]Nenhuma lista intermediária é criada — os valores fluem pelo pipeline um de cada vez.
Escolhendo a Forma Certa
| Você precisa de… | Use |
|---|---|
| Uma lista de valores transformados/filtrados | [expr for item in it] |
| Um dicionário a partir de pares de valores | {k: v for item in it} |
| Uma coleção sem duplicatas | {expr for item in it} |
| Iteração de passagem única eficiente em memória | (expr for item in it) |
| Lógica complexa com múltiplas instruções por item | Loop for simples |
Armadilhas Comuns
Comprehension de dicionário vs. comprehension de conjunto. Ambas usam chaves {}. A diferença está em escrever key: value (dict) ou uma única expression (set). Um {} vazio sempre cria um dict vazio, não um conjunto vazio — use set() para um conjunto vazio.
d = {} # empty dict
s = set() # empty set — NOT {}Chaves sobrescritas. Se a expressão de chave produzir duplicatas, valores posteriores silenciosamente sobrescrevem os anteriores:
data = [("a", 1), ("b", 2), ("a", 99)]
d = {k: v for k, v in data}
print(d) # {'a': 99, 'b': 2} — first 'a' is goneEscopo de variáveis. Em Python 3, a variável do loop em qualquer comprehension é local à comprehension e não vaza para o escopo circundante:
x = "original"
result = {x: x.upper() for x in ["a", "b", "c"]}
print(x) # 'original' — comprehension's x did not overwrite thisLimite de legibilidade. Se você precisar de mais de uma condição if ou a expressão for longa, um loop for simples com nomes de variáveis descritivos costuma ser mais claro:
# Hard to read
result = {k: v for k, v in data.items() if k.startswith("user_") if v is not None}
# Easier to read
result = {}
for k, v in data.items():
if k.startswith("user_") and v is not None:
result[k] = vTópicos Relacionados
- List Comprehension — a base: sintaxe, filtragem, loops aninhados e quando usar um loop
forsimples - Python Dictionaries — conceitos básicos de dicionário, criação e acesso
- Python Sets — criação de conjuntos, operações e casos de uso
- Loop Dictionaries — todas as técnicas para iterar sobre dicionários
- Python Iterators — como funciona o protocolo de iterador do Python internamente