ProgrammazioneSviluppatore Python

Spiega cosa sono i generatori in Python. Come funzionano, a cosa servono e in cosa si differenziano dalle espressioni di lista?

Supera i colloqui con l'assistente IA Hintsage

Risposta.

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:

  • Espressioni di lista ([x for x in range(10)]) creano subito l'intera lista in memoria.
  • Generatori ((x for x in range(10))) creano gli elementi uno alla volta, consumando molta meno memoria.

Quando usare i generatori:

  • Se non è necessario l'accesso agli elementi per indice.
  • Se i dati sono troppo grandi per essere memorizzati in memoria.
  • Quando si organizza l'elaborazione dei dati in streaming (ad esempio, la lettura delle righe di un file).
# Funzione generatrice def counter(n): for i in range(n): yield i for number in counter(5): print(number)

Domanda trabocchetto.

"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

Esempi di errori reali a causa della mancanza di conoscenza delle sottigliezze dell'argomento.


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.