I calcoli "su richiesta" o calcoli pigri sono diventati popolari con l'aumento dei volumi di dati elaborati. In Python, questi meccanismi sono stati implementati nella libreria standard attraverso generatori e iteratori, successivamente tramite la funzione itertools e classi in grado di restituire un elemento alla volta su richiesta, evitando di mantenere tutti i dati in memoria contemporaneamente.
La costruzione normale delle collezioni richiede il caricamento del risultato completo in memoria. Se il volume è grande, il programma potrebbe "bloccare" o funzionare molto lentamente. È importante saper gestire flussi di dati, ad esempio file di molte gigabyte o risultati di richieste API.
I calcoli pigri permettono di ottenere elementi secondo necessità. In Python, ciò è semplificato dall'uso di generatori, dalla sintassi yield, dalle espressioni generatore, dalle funzioni map, filter, zip, oltre che dal modulo itertools. Questo approccio è basato sul protocollo degli iteratori.
Esempio di codice:
def huge_sequence(): for i in range(1, 10**9): yield i * i for val in huge_sequence(): if val > 100: break print(val)
Caratteristiche chiave:
I generatori in Python risparmiano sempre memoria?
Risposta: No, solo se i dati non richiedono realmente una memorizzazione intermedia tra i passaggi. Alcune costruzioni, come le list comprehensions, creano immediatamente l'intera lista, mentre i generatori lo fanno solo su richiesta. Se i risultati intermedi sono comunque necessari, il risparmio si perde.
Esempio:
squares = (x**2 for x in range(10**8)) # pigro, efficiente result = list(squares) # consuma immediatamente tutta la memoria
È corretto che map e filter restituiscano sempre liste?
No, in Python 3 map e filter restituiscono non una lista, ma un iteratore (generatore pigro), il che risparmia memoria e consente di elaborare dati "al volo".
È possibile iterare più volte su un generatore?
No, un generatore "esaurisce" dopo un'iterazione completa. Se è necessaria una nuova passata, è meglio creare un nuovo generatore o utilizzare una collezione contenitore il cui contenuto può essere attraversato più volte.
Un sviluppatore cerca di elaborare un grande file di log caricandolo in memoria come lista di righe.
Vantaggi:
Svantaggi:
Si utilizza un generatore — lettura riga per riga del file con elaborazione di ciascuna riga man mano che viene letta.
Vantaggi:
Svantaggi: