2.30. Compreensões

Uma compreensão constrói uma nova lista, conjunto, dicionário ou gerador a partir de um iterável existente, em uma única expressão. É uma substituição para o padrão comum de começar com um contêiner vazio e ir adicionando em um laço.

2.30.1. Compreensões de lista

squares = [x * x for x in range(5)]
print(squares)

Saída:

[0, 1, 4, 9, 16]

O mesmo laço escrito por extenso:

squares = []
for x in range(5):
    squares.append(x * x)

A forma de compreensão é uma única expressão que constrói a lista no local. Não há squares = [] nem .append – o resultado é o valor da compreensão.

A expressão "[f(x) for x in xs if cond]" anotada: a expressão inicial é o resultado, a cláusula for nomeia a variável do laço, a cláusula if filtra quais itens são mantidos.

A expressão inicial produz cada item; a cláusula for nomeia a variável do laço; um if opcional filtra os itens.

2.30.2. Filtrando com if

Uma cláusula if opcional mantém apenas os itens que correspondem:

evens = [x for x in range(10) if x % 2 == 0]
print(evens)

Saída:

[0, 2, 4, 6, 8]

O filtro é executado antes da expressão inicial – x % 2 == 0 é verificado primeiro; apenas os valores correspondentes chegam a x para a saída.

2.30.3. Compreensões de dicionário e de conjunto

A mesma forma funciona com literais de dicionário e de conjunto.

Uma compreensão de dicionário tem um par key: value antes do for:

squares = {x: x * x for x in range(5)}
print(squares)

Saída:

{0: 0, 1: 1, 2: 4, 3: 9, 4: 16}

Uma compreensão de conjunto usa chaves e uma única expressão:

unique_lengths = {len(w) for w in ["a", "bb", "c", "bb"]}
print(unique_lengths)

Saída:

{1, 2}

2.30.4. Expressões geradoras

Parênteses produzem uma expressão geradora em vez de uma lista. Os valores são calculados um de cada vez, sob demanda:

total = sum(x * x for x in range(1000))

Nenhuma lista de um milhão de itens é construída. Os valores fluem um a um para sum(), que os soma e descarta cada um conforme avança.

Expressões geradoras são a escolha certa ao alimentar valores em uma função de redução (sum(), max(), any(), all()) ou em qualquer outro código que consome iteradores – elas economizam a memória que a lista equivalente teria usado.

2.30.5. Quando não usar uma compreensão

Compreensões são concisas, mas nem sempre mais claras. Recorra a um laço for comum quando:

  • O corpo precisa de mais de uma instrução (uma compreensão comporta exatamente uma expressão).

  • O corpo tem efeitos colaterais (imprimir, escrever em um arquivo) – compreensões servem para construir uma coleção, não para executar ações.

  • O filtro ou a transformação tem tantas partes que a compreensão deixa de ser lida da esquerda para a direita.