Iteratori Python (__iter__ e __next__): come usarlo e perché?

Gli iteratori sono oggetti su cui è possibile iterare. In questo tutorial imparerai come funziona l'iteratore e come puoi costruire il tuo iteratore usando i metodi __iter__ e __next__.

Video: iteratori Python

Iteratori in Python

Gli iteratori sono ovunque in Python. Sono elegantemente implementati all'interno di forloop, comprensioni, generatori ecc. Ma sono nascosti in bella vista.

Iterator in Python è semplicemente un oggetto su cui è possibile iterare. Un oggetto che restituirà dati, un elemento alla volta.

Tecnicamente parlando, un oggetto iteratore Python deve implementare due metodi speciali __iter__()e __next__(), collettivamente chiamati protocollo iteratore .

Un oggetto è chiamato iterabile se possiamo ottenere un iteratore da esso. La maggior parte dei contenitori incorporati in Python come: list, tuple, string ecc. Sono iterabili.

La iter()funzione (che a sua volta chiama il __iter__()metodo) restituisce un iteratore da loro.

Iterazione attraverso un iteratore

Usiamo la next()funzione per scorrere manualmente tutti gli elementi di un iteratore. Quando raggiungiamo la fine e non ci sono più dati da restituire, verrà sollevata l' StopIterationeccezione. Di seguito è riportato un esempio.

 # define a list my_list = (4, 7, 0, 3) # get an iterator using iter() my_iter = iter(my_list) # iterate through it using next() # Output: 4 print(next(my_iter)) # Output: 7 print(next(my_iter)) # next(obj) is same as obj.__next__() # Output: 0 print(my_iter.__next__()) # Output: 3 print(my_iter.__next__()) # This will raise error, no items left next(my_iter)

Produzione

 4 7 0 3 Traceback (la chiamata più recente per ultima): File "", riga 24, nella prossima (my_iter) StopIteration

Un modo più elegante di iterare automaticamente è usare il ciclo for. Usando questo, possiamo iterare su qualsiasi oggetto che può restituire un iteratore, ad esempio elenco, stringa, file ecc.

 >>> for element in my_list:… print(element)… 4 7 0 3

Utilizzo del ciclo for per iteratori

Come si vede nell'esempio sopra, il forciclo è stato in grado di iterare automaticamente attraverso l'elenco.

In effetti il forciclo può iterare su qualsiasi iterabile. Diamo uno sguardo più da vicino a come il forciclo è effettivamente implementato in Python.

 for element in iterable: # do something with element

È effettivamente implementato come.

 # create an iterator object from that iterable iter_obj = iter(iterable) # infinite loop while True: try: # get the next item element = next(iter_obj) # do something with element except StopIteration: # if StopIteration is raised, break from loop break

Quindi internamente, il forciclo crea un oggetto iteratore, iter_objchiamando iter()l'iterabile.

Ironia della sorte, questo forciclo è in realtà un ciclo while infinito.

All'interno del ciclo, chiama next()per ottenere l'elemento successivo ed esegue il corpo del forciclo con questo valore. Dopo che tutti gli elementi si esauriscono, StopIterationviene sollevato che viene catturato internamente e il ciclo termina. Nota che qualsiasi altro tipo di eccezione passerà.

Creazione di iteratori personalizzati

Costruire un iteratore da zero è facile in Python. Dobbiamo solo implementare __iter__()i __next__()metodi e.

Il __iter__()metodo restituisce l'oggetto iteratore stesso. Se necessario, è possibile eseguire alcune inizializzazioni.

Il __next__()metodo deve restituire l'elemento successivo nella sequenza. Al raggiungimento della fine, e nelle chiamate successive, deve rilanciare StopIteration.

Qui, mostriamo un esempio che ci darà la potenza successiva di 2 in ogni iterazione. L'esponente di potenza parte da zero fino a un numero impostato dall'utente.

Se non hai idea della programmazione orientata agli oggetti, visita Python Object-Oriented Programming.

 class PowTwo: """Class to implement an iterator of powers of two""" def __init__(self, max=0): self.max = max def __iter__(self): self.n = 0 return self def __next__(self): if self.n <= self.max: result = 2 ** self.n self.n += 1 return result else: raise StopIteration # create an object numbers = PowTwo(3) # create an iterable from the object i = iter(numbers) # Using next to get to the next iterator element print(next(i)) print(next(i)) print(next(i)) print(next(i)) print(next(i))

Produzione

 1 2 4 8 Traceback (ultima chiamata più recente): File "/home/bsoyuj/Desktop/Untitled-1.py", riga 32, in stampa (next (i)) File "", riga 18, in __next__ raise StopIteration StopIteration

Possiamo anche usare un forciclo per iterare sulla nostra classe iteratore.

 >>> for i in PowTwo(5):… print(i)… 1 2 4 8 16 32

Iteratori infiniti di Python

Non è necessario che l'elemento in un oggetto iteratore debba essere esaurito. Possono esserci infiniti iteratori (che non finiscono mai). Dobbiamo stare attenti quando gestiamo tali iteratori.

Ecco un semplice esempio per dimostrare infiniti iteratori.

The built-in function iter() function can be called with two arguments where the first argument must be a callable object (function) and second is the sentinel. The iterator calls this function until the returned value is equal to the sentinel.

 >>> int() 0 >>> inf = iter(int,1) >>> next(inf) 0 >>> next(inf) 0

We can see that the int() function always returns 0. So passing it as iter(int,1) will return an iterator that calls int() until the returned value equals 1. This never happens and we get an infinite iterator.

We can also build our own infinite iterators. The following iterator will, theoretically, return all the odd numbers.

 class InfIter: """Infinite iterator to return all odd numbers""" def __iter__(self): self.num = 1 return self def __next__(self): num = self.num self.num += 2 return num

A sample run would be as follows.

 >>> a = iter(InfIter()) >>> next(a) 1 >>> next(a) 3 >>> next(a) 5 >>> next(a) 7

And so on…

Be careful to include a terminating condition, when iterating over these types of infinite iterators.

Il vantaggio di utilizzare gli iteratori è che risparmiano risorse. Come mostrato sopra, potremmo ottenere tutti i numeri dispari senza memorizzare l'intero sistema numerico in memoria. Possiamo avere infiniti elementi (teoricamente) nella memoria finita.

C'è un modo più semplice per creare iteratori in Python. Per saperne di più visita: Generatori Python che utilizzano yield.

Articoli interessanti...