2.30. Compreensões

Uma compreensão constrói uma nova lista, conjunto, dicionário ou gerador a partir de um iterável existente, numa única expressão. É uma alternativa ao padrão comum de começar com um contentor vazio e adicionar elementos num ciclo.

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 ciclo escrito explicitamente:

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

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

The expression "[f(x) for x in xs if cond]" annotated: the leading expression is the result, the for clause names the loop variable, the if clause filters which items are kept.

A expressão inicial produz cada item; a cláusula for dá nome à variável de ciclo; um if opcional filtra os itens.

2.30.2. Filtragem 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 conjunto

A mesma forma funciona com literais de dicionário e 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 chavetas e uma expressão única:

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 curvos produzem uma expressão geradora em vez de uma lista. Os valores são calculados um de cada vez, a pedido:

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

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

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

2.30.5. Quando não usar uma compreensão

As compreensões são concisas, mas nem sempre mais claras. Opte por um ciclo for simples quando:

  • O corpo precisa de mais do que uma instrução (uma compreensão cabe exatamente numa expressão).

  • O corpo tem efeitos secundários (imprimir, escrever num ficheiro) – as compreensões são para construir uma coleção, não para executar ações.

  • O filtro ou a transformação tem tantas partes que a compreensão já não se lê da esquerda para a direita.