🚀 Nuova versione beta disponibile! Feedback o problemi? Contattaci

Iteratori in Python

Codegrind Team•Sep 11 2024

Gli iteratori in Python sono oggetti che permettono di iterare su sequenze di dati, come liste, tuple o stringhe, in modo efficiente e con memoria ottimizzata. Gli iteratori forniscono un metodo standard per attraversare gli elementi di una struttura dati, uno alla volta. In questo articolo, vedremo cosa sono gli iteratori, come funzionano e come crearne di personalizzati in Python.

Cos’è un Iteratore?

Un iteratore è un oggetto che implementa i metodi speciali __iter__() e __next__(). Il metodo __iter__() restituisce l’iteratore stesso, mentre __next__() restituisce il prossimo elemento della sequenza. Quando non ci sono più elementi da iterare, __next__() solleva l’eccezione StopIteration.

Esempio di Iteratore con Liste

Molti oggetti in Python, come le liste, le tuple o i dizionari, sono iterabili. Questo significa che puoi usare un ciclo for per attraversarli. Ma dietro le quinte, Python utilizza un iteratore per fare questo:

lista = [1, 2, 3, 4]

# Ottieni l'iteratore
iteratore = iter(lista)

# Usa il metodo __next__() per ottenere gli elementi uno alla volta
print(next(iteratore))  # Output: 1
print(next(iteratore))  # Output: 2
print(next(iteratore))  # Output: 3
print(next(iteratore))  # Output: 4

In questo esempio, la lista lista è un oggetto iterabile, e iter(lista) restituisce un iteratore che può essere utilizzato per iterare sugli elementi della lista. Ogni chiamata a next() restituisce l’elemento successivo.

Iterabili vs. Iteratori

  • Oggetto iterabile: È un oggetto che può restituire un iteratore, come liste, stringhe, o dizionari. Gli iterabili implementano il metodo __iter__(), che restituisce un iteratore.
  • Oggetto iteratore: È un oggetto che sa come attraversare una sequenza di valori, uno alla volta. Gli iteratori implementano sia il metodo __iter__() che __next__().

Esempio di Iterazione con un Ciclo for

Quando utilizzi un ciclo for su un oggetto iterabile, Python chiama automaticamente il metodo __iter__() per ottenere un iteratore e utilizza __next__() per attraversare la sequenza:

lista = [1, 2, 3, 4]

for elemento in lista:
    print(elemento)

Questo ciclo for è equivalente all’uso esplicito di iter() e next():

iteratore = iter(lista)
while True:
    try:
        elemento = next(iteratore)
        print(elemento)
    except StopIteration:
        break

Creare un Iteratore Personalizzato

Puoi creare i tuoi iteratori personalizzati implementando i metodi __iter__() e __next__() in una classe.

Esempio di Iteratore Personalizzato

Supponiamo di voler creare un iteratore che restituisce i numeri da 1 a un numero massimo:

class ContaFinoA:
    def __init__(self, massimo):
        self.massimo = massimo
        self.contatore = 1

    def __iter__(self):
        return self

    def __next__(self):
        if self.contatore <= self.massimo:
            risultato = self.contatore
            self.contatore += 1
            return risultato
        else:
            raise StopIteration

conta = ContaFinoA(5)

for numero in conta:
    print(numero)

Output:

1
2
3
4
5

In questo esempio:

  • La classe ContaFinoA implementa un iteratore personalizzato che restituisce i numeri da 1 fino al massimo specificato.
  • Il metodo __next__() restituisce il numero successivo e solleva l’eccezione StopIteration quando non ci sono più numeri da restituire.

L’uso di iter() e next()

La funzione iter() restituisce un iteratore da un oggetto iterabile. Una volta ottenuto l’iteratore, puoi usare next() per ottenere gli elementi uno alla volta.

Esempio di iter() e next()

stringa = "Python"
iteratore_stringa = iter(stringa)

print(next(iteratore_stringa))  # Output: P
print(next(iteratore_stringa))  # Output: y
print(next(iteratore_stringa))  # Output: t

In questo esempio, la stringa stringa è un iterabile, e iter(stringa) restituisce un iteratore che possiamo attraversare con next().

Iteratori Inesauribili

Alcuni iteratori sono inesauribili e possono generare sequenze infinite di valori. Un esempio classico è la funzione count() del modulo itertools, che genera una sequenza infinita di numeri.

Esempio di Iteratore Inesauribile

import itertools

contatore = itertools.count()

for numero in contatore:
    if numero > 5:
        break
    print(numero)

Output:

0
1
2
3
4
5

In questo esempio, itertools.count() è un iteratore che genera una sequenza infinita di numeri interi. Usiamo break per interrompere il ciclo dopo aver stampato i primi sei numeri.

Funzioni di Utilità con Iteratori

Il modulo itertools di Python offre molte funzioni per lavorare con gli iteratori, come cycle(), repeat(), chain(), e molte altre.

Esempio di itertools.cycle()

La funzione cycle() crea un iteratore che ripete ciclicamente gli elementi di un iterabile.

import itertools

ciclo = itertools.cycle([1, 2, 3])

for _ in range(6):
    print(next(ciclo))

Output:

1
2
3
1
2
3

In questo esempio, itertools.cycle() crea un iteratore che ripete indefinitamente i valori [1, 2, 3].

Esempio di itertools.chain()

La funzione chain() permette di concatenare più iterabili, trattandoli come un unico iteratore.

import itertools

lista1 = [1, 2, 3]
lista2 = [4, 5, 6]

for elemento in itertools.chain(lista1, lista2):
    print(elemento)

Output:

1
2
3
4
5
6

In questo esempio, itertools.chain() unisce lista1 e lista2 in un unico iteratore, iterando su tutti gli elementi in sequenza.

Differenza tra Generatori e Iteratori

Gli iteratori sono oggetti che implementano i metodi __iter__() e __next__(), come abbiamo visto finora. I generatori, invece, sono una forma speciale di iteratori creati con le funzioni generatori che utilizzano la parola chiave yield.

Mentre tutti i generatori sono iteratori, non tutti gli iteratori sono generatori. I generatori sono più semplici da implementare rispetto agli iteratori personalizzati, e sono spesso utilizzati per creare sequenze in modo più leggibile ed efficiente.

Esempio di Generatore

def generatore():
    yield 1
    yield 2
    yield 3

for valore in generatore():
    print(valore)

Output:

1
2
3

Considerazioni Finali

Gli iteratori sono una parte fondamentale di Python, permettendo di iterare su grandi quantità di dati senza doverli caricare tutti in memoria. Creare iteratori personalizzati può semplificare l’elaborazione di sequenze di dati complesse. Con l’aiuto di strumenti come il modulo itertools, è possibile gestire iterazioni complesse in modo efficiente e conciso.