I generatori sono oggetti iterabili speciali in Python che consentono di creare sequenze "al volo", senza occupare memoria per l'intera collezione contemporaneamente. Vengono implementati tramite funzioni con la parola chiave yield o espressioni generatrici ((expr for ... in ...)). Questo è utile quando si lavora con grandi quantità di dati o flussi potenzialmente infiniti.
Differenze chiave dalle espressioni di lista:
[x for x in range(10)]) creano subito l'intera lista in memoria.(x for x in range(10))) creano gli elementi uno alla volta, consumando molta meno memoria.Quando usare i generatori:
# Funzione generatrice def counter(n): for i in range(n): yield i for number in counter(5): print(number)
"Qual è la differenza tra l'uso di una funzione con yield e una funzione normale che restituisce una lista? Fai un esempio."
Risposta:
Una funzione normale calcola e restituisce immediatamente una lista, occupando memoria per tutti i suoi elementi. Una funzione con yield restituisce un generatore, che produce gli elementi uno alla volta e non carica l'intera sequenza in memoria subito.
def make_list(n): return [i for i in range(n)] # Restituisce una lista immediatamente, occupa molta memoria def make_generator(n): for i in range(n): yield i # Restituirà un elemento alla volta
Storia
Nel progetto per analizzare grandi log si utilizzavano espressioni di lista per estrarre le righe contenenti errori:
error_lines = [line for line in open('biglog.txt') if 'ERROR' in line]
Il file superava i 2 GB e l'applicazione è andata in crash con OOM (Out of Memory). Era necessario utilizzare un generatore:
error_lines = (line for line in open('biglog.txt') if 'ERROR' in line)
Storia
Un collaboratore voleva analizzare una lista corta, scrisse una funzione con yield, ma dimenticò che viene restituito un generatore, non una lista:
result = my_generator_function() # result è un generatore, non una lista if len(result) > 5: # TypeError: object of type 'generator' has no len()
Correzione: avvolgere il risultato in list().
Storia
Hanno provato a iterare su un generatore più volte:
numbers = (i for i in range(5)) for n in numbers: pass # esaurito il generatore for n in numbers: print(n) # non stampa nulla
Il generatore è monouso. È necessario crearne uno nuovo per un uso ripetuto.