Iteradores y el Protocolo de Iterador
La iteración está en todas partes en Python: bucles for, comprensiones, desempaquetado y sum. Los objetos participan implementando el protocolo de iterador o delegando a generadores.
Receta
class Countdown:
def __init__(self, start: int) -> None:
self.current = start
def __iter__(self):
return self
def __next__(self) -> int:
if self.current < 0:
raise StopIteration
value = self.current
self.current -= 1
return value
for n in Countdown(3):
print(n)Cuándo usar esto:
- Recorrido de secuencias personalizadas sin almacenar una lista completa
- Procesamiento de datos en streaming, fragmento a fragmento
- Pipelines de transformación perezosa
- Integración con
itertoolsy buclesfor
Ejemplo Funcional
from collections.abc import Iterable, Iterator
class CSVLines:
def __init__(self, lines: Iterable[str]) -> None:
self._lines = iter(lines)
def __iter__(self) -> Iterator[list[str]]:
for raw in self._lines:
yield [cell.strip() for cell in raw.split(",")]
def take(iterator: Iterator[int], n: int) -> list[int]:
result: list[int] = []
for _ in range(n):
try:
result.append(next(iterator))
except StopIteration:
break
return result
if __name__ == "__main__":
rows = CSVLines(["a,b", "c,d"])
for row in rows:
print(row)
print(take(iter(range(10)), 3))Lo que esto demuestra:
- Un iterable puede devolver un generador desde
__iter__(el generador es un iterador) iter()obtiene un iterador;next()extrae elementos hastaStopIterationtakeconsume manualmente un iterador con lectura limitada- Separación entre iterable (reproducible mediante un nuevo iterador) y iterador (un solo paso)
Profundización
Cómo Funciona
- Iterable - Implementa
__iter__devolviendo un iterador. - Iterator - Implementa
__iter__(devuelve self) y__next__. - StopIteration - Señala el final; no debe ser capturado de forma generalizada alrededor de
for. - Funciones generadoras -
yieldcrea un iterador automáticamente. - iter(callable, sentinel) - Llama a callable hasta que se alcance el valor sentinel.
Resumen del Protocolo
| Objeto | Métodos requeridos |
|---|---|
| Iterable | __iter__ |
| Iterator | __iter__, __next__ |
Notas de Python
# iter con sentinel para fragmentos binarios
with open("data.bin", "rb") as fh:
for chunk in iter(lambda: fh.read(4096), b""):
process(chunk)Errores Comunes
- Iterable confundido con iterator - El iterador se agota después de un
for. Solución: Llamar aiter()de nuevo solo en iterables. - Lanzar StopIteration en un generador - En generadores usar
returnsimple; en__next__lanzarStopIteration. - Iterable mutable durante la iteración - Cambios de tamaño de diccionario/lista provocan errores. Solución: Crear una instantánea o posponer eliminaciones.
- getitem para iteración heredada - Protocolo de secuencia antiguo; preferir
__iter__. - Iterador infinito sin break - Bloquea al consumidor. Solución: Documentar o proporcionar
take/islice.
Alternativas
| Alternativa | Usar cuando | No usar cuando |
|---|---|---|
| Función generadora | La mayoría de iteraciones personalizadas | Se necesita estado de clase más allá de yield |
itertools | Álgebra de iteradores estándar | Bucle simple único |
Materializar en list | Necesidad de reutilización/acceso aleatorio | Streams demasiado grandes |
| Iterador asíncrono | async for con I/O | Pipeline solo síncrono |
Preguntas Frecuentes
¿Cuál es la diferencia entre iterable e iterator?
Un iterable produce iteradores. Un iterador produce elementos una vez hasta que se agota.
¿Por qué StopIteration?
Señala el fin de la iteración al mecanismo del bucle for; no está destinado al control de flujo general en el código de la aplicación.
¿Puedo iterar dos veces?
Solo si el objeto es iterable y devuelve un nuevo iterador en cada llamada a iter(). Los generadores se agotan después de un solo paso.
¿Qué hace el bucle for?
it = iter(obj); while True: try: x = next(it) ... except StopIteration: break
¿La forma de iter() con dos argumentos?
Llama repetidamente a la función hasta que se alcanza el sentinel; útil para leer streams.
¿collections.abc Iterable?
Comprobación estructural de isinstance; las clases personalizadas pueden registrarse o implementar __iter__.
¿Clase generadora vs. iteradora?
Los generadores son más rápidos de escribir; las clases se usan cuando se necesita estado complejo o múltiples métodos.
¿Iteración con enumerate y zip?
Las funciones integradas devuelven iteradores; se consumen una vez, eficientes en memoria.
¿Iteración asíncrona?
__aiter__/__anext__ con async for; ver la sección de asyncio.
¿Cómo probar iteradores?
list(iterator) para materializar lo esperado; afirmar StopIteration en llamadas next adicionales.
Relacionado
- Generadores y yield - iteradores basados en yield
- itertools - utilidades de iteradores
- Comprensiones y Expresiones Generadoras - sintaxis de genexp
- Clases Base Abstractas - ABC de Iterable
Versiones de la pila: Esta página fue escrita para Python 3.14.0 (estable 3.14, mantenimiento 3.13), FastAPI 0.115+, Django 5.2, Flask 3.1, Pydantic 2, PyTorch 2.6+, pandas 2.2+, Polars 1.x, ruff 0.9+, y uv 0.6+.