2.30. Comprehension

Una comprehension costruisce una nuova lista, set, dict o generatore a partire da un iterabile esistente, in un’unica espressione. È un sostituto del pattern comune che parte da un contenitore vuoto e aggiunge elementi in un ciclo.

2.30.1. List comprehension

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

Output:

[0, 1, 4, 9, 16]

Lo stesso ciclo scritto per esteso:

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

La forma a comprehension è un’unica espressione che costruisce la lista sul posto. Non c’è alcun squares = [] e nessun .append – il risultato è il valore della comprehension.

L'espressione "[f(x) for x in xs if cond]" annotata: l'espressione iniziale è il risultato, la clausola for nomina la variabile di ciclo, la clausola if filtra quali elementi vengono mantenuti.

L’espressione iniziale produce ogni elemento; la clausola for nomina la variabile di ciclo; una clausola if opzionale filtra via gli elementi.

2.30.2. Filtrare con if

Una clausola if opzionale mantiene solo gli elementi che corrispondono:

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

Output:

[0, 2, 4, 6, 8]

Il filtro viene eseguito prima dell’espressione iniziale – x % 2 == 0 viene verificato per primo; solo i valori corrispondenti raggiungono x per l’output.

2.30.3. Dict e set comprehension

La stessa struttura funziona con i letterali dict e set.

Una dict comprehension ha una coppia key: value prima del for:

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

Output:

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

Una set comprehension usa le parentesi graffe e un’unica espressione:

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

Output:

{1, 2}

2.30.4. Espressioni generatrici

Le parentesi tonde producono un”espressione generatrice anziché una lista. I valori vengono calcolati uno alla volta, su richiesta:

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

Non viene mai costruita alcuna lista da un milione di elementi. I valori fluiscono uno per uno in sum(), che li somma e scarta ciascuno man mano che procede.

Le espressioni generatrici sono la scelta giusta quando si alimentano valori in una funzione di riduzione (sum(), max(), any(), all()) o in qualsiasi altro codice che consuma iteratori – risparmiano la memoria che la lista equivalente avrebbe utilizzato.

2.30.5. Quando non usare una comprehension

Le comprehension sono concise ma non sempre più chiare. Ricorri a un semplice ciclo for quando:

  • Il corpo necessita di più di un’istruzione (una comprehension contiene esattamente un’espressione).

  • Il corpo ha effetti collaterali (stampa, scrittura su file) – le comprehension servono a costruire una collezione, non a eseguire azioni.

  • Il filtro o la trasformazione ha così tante parti che la comprehension non si legge più da sinistra a destra.