Uma vez que as compreensões de lista fazem sentido, você nunca mais vai escrever um loop for que anexa a uma lista. Elas não são apenas açúcar sintático — elas sinalizam a intenção claramente e são executadas mais rapidamente do que o loop equivalente no CPython graças à otimização de bytecode. De acordo com o tutorial oficial do Python, compreensões fornecem uma maneira concisa de criar listas com base em sequências existentes ou outros iteráveis. Este artigo constrói o modelo mental primeiro, depois cobre cada padrão real: filtragem, aninhamento, compreensões de dict e set, e o único caso em que você deve usar um loop for simples.

O Padrão Básico

A anatomia de uma compreensão de lista é [expressão for item in iterável]. Três partes: a expressão de saída (o que cada elemento se torna), a variável de loop e o iterável do qual extrair. Comece com um loop familiar e comprima-o.

python
# Plain for loop — building a list of file sizes in KB
file_sizes_bytes = [1024, 204800, 51200, 3145728, 8192]

sizes_kb = []
for size in file_sizes_bytes:
    sizes_kb.append(size / 1024)

# print(sizes_kb)  →  [1.0, 200.0, 50.0, 3072.0, 8.0]

# Same result as a list comprehension — one line, same meaning
sizes_kb = [size / 1024 for size in file_sizes_bytes]

A compreensão lê quase como inglês: "me dê size / 1024 para cada size em file_sizes_bytes." Essa clareza é o ganho real — um leitor não precisa rastrear uma chamada append para entender o que você está construindo.

python
# Another common pattern: deriving one list from another
usernames = ["alice", "bob", "carol"]

# Build a list of display names
display_names = [name.capitalize() for name in usernames]
# ['Alice', 'Bob', 'Carol']

# Or extract a single field from a list of dicts
users = [
    {"id": 1, "name": "Alice", "role": "admin"},
    {"id": 2, "name": "Bob",   "role": "viewer"},
    {"id": 3, "name": "Carol", "role": "editor"},
]

names = [user["name"] for user in users]
# ['Alice', 'Bob', 'Carol']

Filtrando com if

Adicione uma condição no final e apenas os elementos que passarem entrarão na lista de saída: [expressão for item in iterável if condição]. Este é o padrão que substitui uma combinação for + if + append.

python
# Loop version — extract only active users
active_users = []
for user in users:
    if user["active"]:
        active_users.append(user["name"])

# Comprehension version — identical result
users = [
    {"name": "Alice", "active": True},
    {"name": "Bob",   "active": False},
    {"name": "Carol", "active": True},
    {"name": "Dave",  "active": False},
]

active_users = [user["name"] for user in users if user["active"]]
# ['Alice', 'Carol']
python
# Filtering a raw CSV row — drop blanks and whitespace-only values
raw_row = ["[email protected]", "", "  ", "editor", " "]

clean_row = [field.strip() for field in raw_row if field.strip()]
# ['[email protected]', 'editor']

# Filtering a list of log levels
log_lines = [
    "INFO  server started",
    "DEBUG loading config",
    "ERROR database timeout",
    "DEBUG query took 450ms",
    "ERROR disk space low",
]

errors = [line for line in log_lines if line.startswith("ERROR")]
# ['ERROR database timeout', 'ERROR disk space low']

Trabalhando com Strings

O processamento de strings é onde as compreensões se destacam. A combinação dos ricos métodos de string do Python e a sintaxe de compreensão mantém os pipelines de transformação legíveis sem variáveis intermediárias.

python
# Strip whitespace from tags coming out of a form field
raw_tags = ["  python ", "data science", " machine-learning ", "API"]

tags = [tag.strip().lower() for tag in raw_tags]
# ['python', 'data science', 'machine-learning', 'api']

# Normalise email addresses from a signup CSV
raw_emails = ["[email protected]", "  [email protected]  ", "[email protected]"]

emails = [e.strip().lower() for e in raw_emails]
# ['[email protected]', '[email protected]', '[email protected]']

# Extract file extensions from a list of uploaded filenames
filenames = ["report.pdf", "avatar.PNG", "data.CSV", "archive.tar.gz", "notes.txt"]

extensions = [name.rsplit(".", 1)[-1].lower() for name in filenames if "." in name]
# ['pdf', 'png', 'csv', 'gz', 'txt']
Dica: Ao normalizar entrada do usuário, remova espaços antes de colocar em minúsculas — caso contrário, um valor como " [email protected] " passa uma verificação .lower() no conteúdo mas ainda carrega espaços iniciais que quebrarão uma consulta ao banco de dados ou correspondência de chave JSON.

Compreensões de Dict e Set

A mesma ideia se estende a dicts e sets. Uma compreensão de dict usa chaves com um par chave: valor: {chave: valor for item in iterável}. Uma compreensão de set elimina o dois pontos e produz uma coleção deduplicada: {expressão for item in iterável}.

python
# Invert a dict — swap keys and values
permissions = {"alice": "admin", "bob": "viewer", "carol": "editor"}

by_role = {role: name for name, role in permissions.items()}
# {'admin': 'alice', 'viewer': 'bob', 'editor': 'carol'}

# Build a fast lookup dict from a list of user records
users = [
    {"id": 101, "name": "Alice", "active": True},
    {"id": 102, "name": "Bob",   "active": False},
    {"id": 103, "name": "Carol", "active": True},
]

# O(1) lookups by ID — much faster than scanning the list every time
user_by_id = {user["id"]: user for user in users}
# {101: {...}, 102: {...}, 103: {...}}

# Access a user directly
user_by_id[102]["name"]  # 'Bob'
python
# Set comprehension — deduplicate a list of file extensions
uploads = ["report.pdf", "data.csv", "summary.pdf", "export.CSV", "notes.txt"]

unique_extensions = {name.rsplit(".", 1)[-1].lower() for name in uploads if "." in name}
# {'pdf', 'csv', 'txt'}  — order not guaranteed

Uma ressalva com sets: a ordem não é preservada. Se você precisa deduplicar uma lista enquanto mantém a ordem original, uma compreensão de set é a ferramenta errada — use list(dict.fromkeys(items)) em vez disso, que aproveita o comportamento ordenado por inserção de dicts no Python 3.7+.

Compreensões Aninhadas

Você pode aninhar compreensões para iterar sobre estruturas aninhadas. O caso de uso mais comum é achatar uma lista de listas — uma matriz de uma análise CSV, páginas de resposta de API fragmentadas ou resultados de consulta agrupados.

python
# Flatten a 2D list (e.g. paginated API results)
pages = [
    [{"id": 1, "name": "Alice"}, {"id": 2, "name": "Bob"}],
    [{"id": 3, "name": "Carol"}],
    [{"id": 4, "name": "Dave"}, {"id": 5, "name": "Eve"}],
]

all_users = [user for page in pages for user in page]
# [{'id': 1, ...}, {'id': 2, ...}, {'id': 3, ...}, {'id': 4, ...}, {'id': 5, ...}]

# The order mirrors what nested for loops would produce:
# for page in pages:
#     for user in page:
#         all_users.append(user)

Leia compreensões aninhadas da esquerda para a direita — o loop externo vem primeiro, o interno segundo. Isso corresponde à ordem de loops for aninhados.

python
# Two levels — fine and readable
matrix = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
flat = [cell for row in matrix for cell in row]
# [1, 2, 3, 4, 5, 6, 7, 8, 9]

# Three levels — stop here. Use a plain loop or a helper function.
# This is the point where readability dies:
cube = [[[1,2],[3,4]],[[5,6],[7,8]]]

# Don't do this:
flat3 = [val for layer in cube for row in layer for val in row]

# Do this instead:
flat3 = []
for layer in cube:
    for row in layer:
        flat3.extend(row)
Expressões geradoras usam a mesma sintaxe com parênteses em vez de colchetes: (user["id"] for user in users). Elas avaliam de forma preguiçosa — os elementos são produzidos um por vez em vez de construir a lista completa na memória. Use-as quando você só precisa iterar uma vez, ou quando você está passando o resultado diretamente para sum(), max(), any() ou funções similares. Veja a referência de expressões geradoras para os detalhes completos.

O Ternário if/else Dentro de uma Compreensão

Quando você precisa transformar elementos em um de dois valores — em vez de eliminá-los — use uma expressão ternária na posição de saída. A posição importa: o ternário pertence ao início, não após o iterável.

python
# Correct: ternary is the output expression
users = [
    {"name": "Alice", "active": True},
    {"name": "Bob",   "active": False},
    {"name": "Carol", "active": True},
]

status = ["active" if user["active"] else "inactive" for user in users]
# ['active', 'inactive', 'active']

# Normalise a config value — replace None with a default
raw_config = ["/var/log", None, "/tmp", None, "/etc/app"]

paths = [path if path is not None else "/var/log/default" for path in raw_config]
# ['/var/log', '/var/log/default', '/tmp', '/var/log/default', '/etc/app']
python
# Common mistake — putting the ternary after the iterable (SyntaxError)
# status = ["active" for user in users if user["active"] else "inactive"]  # WRONG

# The trailing "if" is a filter — it drops non-matching items entirely.
# The ternary ("if ... else ...") in the output expression transforms all items.
# They serve different purposes. You can combine them:

# Keep only users, but show their status
status = ["active" if user["active"] else "inactive"
          for user in users
          if user["name"] != "Bob"]
# ['active', 'active']  — Bob was filtered out entirely

Quando Usar um Loop for em vez Disso

Compreensões de lista são para construir listas. Se o que você realmente está fazendo é executar efeitos colaterais, use um loop for — não uma compreensão. Isso importa além do estilo: uma compreensão que descarta seu resultado desperdiça memória construindo uma lista que ninguém usa e esconde a intenção do leitor.

python
# Bad — comprehension for side effects (writing to a file, printing, calling an API)
[print(f"Processing user {user['name']}") for user in users]   # don't do this
[requests.post("/api/notify", json=user) for user in users]    # definitely don't do this

# Good — plain for loop makes the intent obvious
for user in users:
    print(f"Processing user {user['name']}")

for user in users:
    requests.post("/api/notify", json=user)
python
# Bad — comprehension with logic complex enough to need comments or multiple steps
result = [
    user["name"].strip().lower()
    if user.get("active") and user.get("email_verified")
    else user["name"].strip().lower() + " (unverified)"
    for user in users
    if user.get("role") in ("admin", "editor") and user.get("last_login") is not None
]

# Good — break it out when the logic is this involved
result = []
for user in users:
    if user.get("role") not in ("admin", "editor"):
        continue
    if user.get("last_login") is None:
        continue
    name = user["name"].strip().lower()
    if not (user.get("active") and user.get("email_verified")):
        name += " (unverified)"
    result.append(name)
  • Use uma compreensão quando você está construindo uma nova lista a partir de um iterável existente com uma expressão clara e um filtro opcional.
  • Use um loop for quando você está executando efeitos colaterais — I/O, chamadas de rede, impressão, mutação de estado externo.
  • Use um loop for quando a lógica de transformação precisa de várias linhas, variáveis intermediárias ou comentários para ser compreendida.
  • Use uma expressão geradora quando você só precisa iterar uma vez ou passar diretamente para sum(), any(), max() — sem necessidade de lista na memória.

Nota de Desempenho

Compreensões de lista são significativamente mais rápidas do que um loop equivalente for + append no CPython. A razão é o bytecode: uma compreensão compila para um opcode dedicado LIST_APPEND que evita a pesquisa de atributo em list.append a cada iteração. O wiki de dicas de desempenho do Python cobre isso, e a diferença é tipicamente de 10–40% para cargas de trabalho Python puras dependendo do tamanho da lista.

python
import timeit

data = list(range(100_000))

# for + append
def with_loop():
    result = []
    for x in data:
        result.append(x * 2)
    return result

# list comprehension
def with_comprehension():
    return [x * 2 for x in data]

# generator expression — no list built at all
def with_generator():
    return sum(x * 2 for x in data)

# Typical results on CPython 3.12:
# with_loop():          ~7.2 ms
# with_comprehension(): ~4.8 ms  (~33% faster)
# with_generator():     ~4.1 ms  (and uses O(1) memory vs O(n))

Se você não precisa de uma lista materializada — você está passando o resultado para sum(), any(), max(), ou iterando-o uma vez — use uma expressão geradora em vez disso. Ela usa memória constante independentemente do tamanho da entrada, o que importa ao processar grandes exportações CSV ou payloads JSON em um loop apertado.

Conclusão

Compreensões de lista são um daqueles recursos Python que parecem estranhos por cerca de uma semana e depois se tornam impossíveis de viver sem. O modelo mental é simples: expressão de saída, variável de loop, iterável, filtro opcional. Atenha-se a isso e você escreverá Python legível e idiomático. Quando a lógica fica complexa, esse é o sinal para voltar para um loop simples — não um fracasso, apenas a ferramenta certa para o trabalho.

Se você está trabalhando com dados JSON em Python — transformando respostas de API em listas de valores, extraindo campos de registros, construindo dicts de pesquisa — as ferramentas neste site combinam bem com o que você acabou de aprender. Experimente o Formatador JSON para inspecionar e imprimir payloads JSON antes de escrever a compreensão que os processa, ou o Formatador CSV para validar dados CSV antes de analisá-los em uma lista de linhas. Para a referência completa da linguagem, os documentos Python sobre compreensões de lista e o PEP 202 (a proposta original) valem a leitura.