Le espressioni di lista (list comprehensions) sono un modo conciso per creare liste basate su oggetti iterabili esistenti utilizzando una sintassi breve:
squares = [x**2 for x in range(10)]
Questa notazione è equivalente a:
squares = [] for x in range(10): squares.append(x**2)
Le list comprehensions hanno diversi vantaggi:
evens = [x for x in range(10) if x % 2 == 0];L'analogo con map():
def f(x): return x**2 squares = list(map(f, range(10)))
map è più veloce su grandi dati, se si utilizza una funzione già esistente in C, ed è adatto per applicare una funzione a tutti gli elementi. La list comprehension è per qualsiasi espressione, non solo per funzioni predefinite. Il ciclo for è più flessibile, ma più ingombrante.
Perché nell'espressione del tipo
[x for x in range(10)]la variabile x dopo l'esecuzione della lista risulta accessibile al di fuori dell'espressione in Python2, ma non in Python3?
Risposta: In Python2 la variabile di ciclo (x) mantiene il suo valore dopo l'esecuzione della list comprehension. In Python3 — essa viene "isolata" e non è accessibile al di fuori della lista, prevenendo effetti collaterali indesiderati.
Esempio:
# Python 2.x: [x for x in range(3)] print(x) # x == 2 # Python 3.x: [x for x in range(3)] print(x) # NameError: name 'x' is not defined
Storia 1
Uno sviluppatore in un grande progetto voleva filtrare e creare una nuova lista tramite list comprehension:
my_list = [item.transform() for item in data if item.is_valid()]
Ma l'operazione item.transform() generava un errore se item.is_valid() restituiva False. Tuttavia, la funzione di controllo era scritta con un potenziale side effect, e alla fine la list comprehension rompeva in modo poco chiaro parti del codice con effetti collaterali.
Storia 2
Nel progetto durante la migrazione da Python2 a Python3, uno sviluppatore era sicuro che la variabile di ciclo sarebbe rimasta accessibile:
[x for x in range(5)] print(x) # Si aspettava di ottenere 4, ma ha ottenuto NameError.
Questo ha causato un bug nella logica ciclica, dove la variabile doveva rimanere disponibile al di fuori della comprehension.
Storia 3
Utilizzo di espressioni di lista nidificate senza indicazione esplicita dei livelli:
def flatten(matrix): return [cell for row in matrix for cell in row]
I principianti incontrano spesso errori a causa dell'ordine errato di attraversamento (ad esempio, [cell for cell in row for row in matrix] o un annidamento eccessivo), che porta a un risultato errato — una lista unidimensionale invece di una bidimensionale o viceversa.