Lección 7 · 7 de 20
El protocolo de los iteradores · Python
Cómo funciona el for por dentro: iter(), next(), StopIteration y una clase propia con __iter__ y __next__ que pagina resultados en Python.

Un for no sabe nada de listas: le pide a la colección un iterador con iter() y luego llama a next() una y otra vez hasta que salta la excepción `StopIteration`. El programa lo hace a mano con ["ok", "fail"]: dos next() devuelven los dos valores y el tercero lanza StopIteration, que se captura.
Cualquier clase puede participar en este protocolo con dos métodos especiales. __iter__ devuelve el iterador; en Paginator devuelve self, porque el propio objeto lleva la cuenta de por dónde va.
__next__ devuelve el siguiente elemento: corta una página de self.items desde self.start, avanza self.start y la devuelve. Cuando start llega al final, lanza StopIteration y el for termina sin error. La última página tiene solo dos pedidos porque el corte de una lista nunca se sale del final.
Como Paginator cumple el protocolo, funciona con todo lo que recorre: for, enumerate(..., start=1) para numerar las páginas, list() para contarlas, sum(), comprensiones…
Este iterador se gasta, igual que un generador: si recorre el mismo Paginator dos veces, la segunda no da nada. Si quiere poder repetir, haga que __iter__ cree un objeto nuevo cada vez o escriba __iter__ como generador con yield.
Código · main.py
class Paginator: """Iterates over a list of results, one page at a time.""" def __init__(self, items, page_size): self.items = items self.page_size = page_size self.start = 0 def __iter__(self): return self # this object is its own iterator def __next__(self): if self.start >= len(self.items): raise StopIteration # tells for that there is no more page = self.items[self.start:self.start + self.page_size] self.start += self.page_size return pageorders = [f"#{n}" for n in range(1001, 1011)]# What a for loop does behind the scenes: iter() and next()it = iter(["ok", "fail"])print(next(it), next(it))try: next(it)except StopIteration: print("StopIteration: the list is used up")for number, page in enumerate(Paginator(orders, 4), start=1): print(f"Page {number}: {page}")# Any function that loops accepts our iteratorprint("Pages:", len(list(Paginator(orders, 3))))Salida · python3 main.py
ok fail StopIteration: the list is used up Page 1: ['#1001', '#1002', '#1003', '#1004'] Page 2: ['#1005', '#1006', '#1007', '#1008'] Page 3: ['#1009', '#1010'] Pages: 4
Para recordar
forusaiter()una vez ynext()hastaStopIteration.__iter__devuelve el iterador;__next__da el siguiente valor.- Lanzar
StopIterationes la forma de decir que no hay más. - Un objeto iterable funciona con
for,list(),sum(),enumerate()…
Ejercicio
Añada a Paginator un parámetro limit que corte la iteración después de ese número de páginas, aunque queden elementos.