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:
__iter__.__next__ che __iter__ (che restituisce self).__iter__ è efficace se si desidera più passaggi indipendenti sulla collezione.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)
__next__ senza __iter__ (o viceversa).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:
Caso positivo:
Ogni iteratore memorizza il proprio stato nell'istanza creata in __iter__.
Pro: