ProgrammazioneSviluppatore Backend

Che cos'è un oggetto iterabile in Python e come implementare correttamente un oggetto iterabile personalizzato?

Supera i colloqui con l'assistente IA Hintsage

Risposta

Storia della questione: Il concetto di iterabilità è emerso in Python per unificare il lavoro con le collezioni: liste, dizionari, insiemi, ecc. Qualsiasi oggetto su cui si può iterare con un ciclo for è considerato iterabile. Questo è realizzato tramite determinati metodi magici.

Problema: Python richiede protocolli specifici per il corretto funzionamento dei cicli e delle funzioni legate alle sequenze. Se un utente implementa in modo errato questi protocolli nella propria classe, i meccanismi standard (for, list(), sum(), ecc.) potrebbero non funzionare o comportarsi in modo imprevisto.

Soluzione: Un oggetto è iterabile se implementa il metodo __iter__. Un iteratore è colui che ha il metodo __next__ e __iter__, che restituisce self. Di solito, l'oggetto restituito da __iter__ è un iteratore, ma non è obbligatorio. Esempio:

class MyRange: def __init__(self, start, end): self.start = start self.end = end def __iter__(self): self.current = self.start return self def __next__(self): if self.current < self.end: val = self.current self.current += 1 return val raise StopIteration for x in MyRange(1, 4): print(x) # 1, 2, 3

Caratteristiche chiave:

  • L'iterabilità è determinata dalla presenza del metodo __iter__.
  • L'iteratore deve implementare sia __next__ che __iter__ (che restituisce self).
  • Restituire un nuovo oggetto in __iter__ è efficace se si desidera più passaggi indipendenti sulla collezione.

Domande ingannevoli.

Il metodo next è obbligatorio per ogni oggetto iterabile?

No. Per un oggetto iterabile, è necessario solo __iter__, che restituisce un iteratore. __next__ è implementato solo dall'iteratore stesso. Ad esempio, un list non ha il metodo __next__, ma è iterabile: il suo __iter__ restituisce un'istanza dell'iteratore.

lst = [1, 2, 3] print(hasattr(lst, '__next__')) # False

Un oggetto può essere un iteratore di per sé?

Sì, se implementa entrambi i metodi — sia __iter__ che __next__.

È possibile creare più iteratori con stati indipendenti per una singola collezione?

Sì, se __iter__ restituisce ogni volta un nuovo oggetto iteratore.

class MyList: def __init__(self, data): self.data = data def __iter__(self): return iter(self.data)

Errori tipici e anti-pattern

  • Implementare solo __next__ senza __iter__ (o viceversa).
  • Memorizzazione dello stato a livello di classe anziché di istanza, il che porta a bug durante passaggi ripetuti.

Esempio dalla vita reale

Caso negativo: La classe dell'iteratore memorizza lo stato (ad esempio, l'indice corrente) a livello di classe e non di istanza, causando conflitti tra le iterazioni parallele. Pro:

  • Meno memoria (solo un indice). Contro:
  • Errori durante l'iterazione simultanea con due cicli.

Caso positivo: Ogni iteratore memorizza il proprio stato nell'istanza creata in __iter__. Pro:

  • Corretto funzionamento di più iteratori. Contro:
  • Leggermente maggiore consumo di memoria.