Voltar ao Glossario
Glossario Python

List Comprehension em Python: O que É e Como Funciona | Python Brasil

Aprenda list comprehension em Python: sintaxe, dict/set comprehension, walrus operator, performance vs map/filter e quando usar loops. Guia completo.

O que é List Comprehension?

List comprehension é uma forma concisa e elegante de criar listas em Python. Em vez de usar loops tradicionais com append(), você expressa a transformação e o filtro em uma única linha, tornando o código mais pythonico, mais legível e geralmente mais rápido.

É um dos recursos mais apreciados por desenvolvedores Python e está presente em praticamente todo projeto profissional — de scripts simples a bibliotecas como NumPy e Pandas, que se inspiraram nessa sintaxe.

Sintaxe básica

nova_lista = [expressao for item in iteravel if condicao]

As partes for item in iteravel e if condicao seguem exatamente a mesma semântica de um loop for e if normais. A expressao define o que vai para a lista resultante.

Exemplos práticos

# Forma tradicional com loop
quadrados = []
for n in range(10):
    quadrados.append(n ** 2)

# Forma pythonica com list comprehension
quadrados = [n ** 2 for n in range(10)]
# [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

# Com filtro (condicao)
pares = [n for n in range(20) if n % 2 == 0]
# [0, 2, 4, 6, 8, 10, 12, 14, 16, 18]

# Combinando transformacao e filtro
quadrados_de_impares = [n ** 2 for n in range(10) if n % 2 != 0]
# [1, 9, 25, 49, 81]

# Trabalhando com strings
nomes = ['  ana  ', 'BRUNO', 'Carla']
normalizados = [nome.strip().title() for nome in nomes]
# ['Ana', 'Bruno', 'Carla']

# if/else dentro da expressao (ternario)
classificados = ['par' if n % 2 == 0 else 'impar' for n in range(6)]
# ['par', 'impar', 'par', 'impar', 'par', 'impar']

Dict comprehension e Set comprehension

A mesma sintaxe se estende para criar dicionários e conjuntos:

# Dict comprehension — {chave: valor for ...}
quadrados_dict = {n: n ** 2 for n in range(1, 6)}
# {1: 1, 2: 4, 3: 9, 4: 16, 5: 25}

# Inverter um dicionario
original = {'a': 1, 'b': 2, 'c': 3}
invertido = {v: k for k, v in original.items()}
# {1: 'a', 2: 'b', 3: 'c'}

# Filtrar um dicionario
alunos = {'Ana': 9.0, 'Bruno': 5.5, 'Carla': 7.8, 'Diego': 4.9}
aprovados = {nome: nota for nome, nota in alunos.items() if nota >= 6.0}
# {'Ana': 9.0, 'Carla': 7.8}

# Set comprehension — {expressao for ...} — elimina duplicatas
letras = {letra.lower() for letra in 'Abracadabra'}
# {'a', 'b', 'c', 'd', 'r'} — ordem não garantida

Comprehensions aninhadas

É possível usar múltiplos for dentro de uma comprehension. A ordem dos for corresponde à ordem de aninhamento em loops tradicionais:

# Produto cartesiano (equivalente a dois loops aninhados)
pares = [(x, y) for x in range(3) for y in range(3)]
# [(0,0), (0,1), (0,2), (1,0), (1,1), (1,2), (2,0), (2,1), (2,2)]

# Flatten — achatar lista de listas
listas = [[1, 2, 3], [4, 5], [6, 7, 8, 9]]
plana = [item for sublista in listas for item in sublista]
# [1, 2, 3, 4, 5, 6, 7, 8, 9]

# Transpor uma matriz
matriz = [[1, 2, 3],
          [4, 5, 6],
          [7, 8, 9]]
transposta = [[linha[i] for linha in matriz] for i in range(len(matriz[0]))]
# [[1, 4, 7], [2, 5, 8], [3, 6, 9]]
# Nota: para matrizes reais, use numpy.transpose()

# CUIDADO com aninhamento excessivo — dificulta a leitura
# Se precisar de mais de dois 'for', prefira loops explícitos

Walrus operator (:=) em comprehensions — Python 3.8+

O operador walrus (:=) permite atribuir e usar um valor dentro de uma expressão, evitando cálculos duplicados:

# Sem walrus — calcula processar(x) duas vezes
resultados = [processar(x) for x in dados if processar(x) is not None]

# Com walrus — calcula uma vez, reutiliza
resultados = [r for x in dados if (r := processar(x)) is not None]

# Exemplo concreto: aplicar transformação custosa e filtrar
import math

numeros = range(-5, 10)
raizes_validas = [raiz for n in numeros if (raiz := math.sqrt(n)) > 2]
# (so funciona para n >= 0 — aqui simplificado para ilustracao)

# Capturar resultado de regex
import re

textos = ['email: ana@example.com', 'sem email', 'email: bruno@test.org']
emails = [m.group(1) for t in textos
          if (m := re.search(r'email: (.+)', t))]
# ['ana@example.com', 'bruno@test.org']

Performance: comprehension vs map/filter vs loop

List comprehensions são geralmente mais rápidas que loops com append() porque o mecanismo de iteração é implementado em C internamente. A comparação com map()/filter() depende do contexto:

import timeit

n = 10_000

# Loop tradicional
def com_loop():
    resultado = []
    for i in range(n):
        if i % 2 == 0:
            resultado.append(i ** 2)
    return resultado

# List comprehension
def com_comprehension():
    return [i ** 2 for i in range(n) if i % 2 == 0]

# map + filter com lambda
def com_map_filter():
    return list(map(lambda x: x ** 2, filter(lambda x: x % 2 == 0, range(n))))

# Resultados típicos (variam por hardware):
# loop:            ~1.8ms
# comprehension:   ~1.1ms  (30-40% mais rapido que loop)
# map+filter:      ~1.4ms  (map sem lambda pode ser mais rapido)

# Regra geral:
# - List comprehension é mais rápida que loop com append
# - map(funcao_builtin, ...) pode superar comprehension (ex: map(str, nums))
# - map com lambda costuma ser mais lento que comprehension

Padrões comuns

# Remover duplicatas preservando ordem
vistos = set()
sem_duplicatas = [x for x in [3, 1, 2, 1, 3, 4] if not (x in vistos or vistos.add(x))]
# [3, 1, 2, 4]

# Extrair valores de lista de dicionários
usuarios = [{'nome': 'Ana', 'ativo': True}, {'nome': 'Bruno', 'ativo': False}]
nomes_ativos = [u['nome'] for u in usuarios if u['ativo']]
# ['Ana']

# Converter tipos
strings_numeros = ['1', '2', '3', '4', '5']
inteiros = [int(s) for s in strings_numeros]
# [1, 2, 3, 4, 5]

# Gerar tabela de multiplicacao
tabela = {(i, j): i * j for i in range(1, 6) for j in range(1, 6)}
print(tabela[(3, 4)])   # 12

# Filtrar e transformar arquivos
from pathlib import Path
csvs = [str(p) for p in Path('.').iterdir() if p.suffix == '.csv']

Quando usar loop em vez de comprehension

List comprehension não é sempre a melhor escolha. Prefira loops tradicionais quando:

# 1. A logica tem efeitos colaterais importantes
# Ruim — efeito colateral em comprehension
[print(x) for x in range(10)]   # funciona mas e anti-padrao

# Bom
for x in range(10):
    print(x)

# 2. Ha multiplas etapas com logica complexa
# Ruim — ilegivel
resultado = [transformar(validar(normalizar(x))) for x in dados if checar(x) and outro(x)]

# Bom — use loop ou quebra em funcoes
def processar(x):
    if not checar(x) or not outro(x):
        return None
    return transformar(validar(normalizar(x)))

resultado = [r for x in dados if (r := processar(x)) is not None]

# 3. Voce precisa de break ou continue com logica complexa
# (comprehensions nao suportam break/continue)
for x in dados:
    if x < 0:
        break   # para completamente
    resultado.append(x ** 2)

Diretrizes de legibilidade

  • Mantenha comprehensions em uma linha quando possível.
  • Se passar de 79 caracteres (PEP 8), quebra em múltiplas linhas com indentação.
  • Dois for aninhados são aceitáveis; três ou mais pedem um loop explícito.
  • O nome da variável importa: [x for x in lista] é menos claro que [nome for nome in nomes].
# Quebra de linha adequada para comprehensions longas
resultado = [
    transformar(item)
    for item in colecao_grande
    if item.valido and item.ativo
]

Termos Relacionados

  • Generators — Versão lazy (avaliação preguiçosa) das list comprehensions
  • Lambda — Funções anônimas frequentemente usadas com map/filter
  • Dicionário — Dict comprehension segue a mesma lógica