El ciclo for-in en Python se implementa a través del protocolo de iteración, basado en dos métodos especiales: __iter__() y __next__(). Cuando el ciclo comienza, Python llama a iter(obj) (busca el método __iter__). Si se devuelve un objeto iterador que tiene definido __next__(), el ciclo comienza a llamar a este método para obtener cada siguiente elemento.
Si el elemento se emite con éxito, la iteración continúa. Cuando se lanza la excepción StopIteration, el ciclo finaliza su ejecución.
Ejemplo de un iterador propio:
class Counter: def __init__(self, low, high): self.current = low self.high = high def __iter__(self): return self def __next__(self): if self.current > self.high: raise StopIteration else: self.current += 1 return self.current - 1 for num in Counter(1, 3): print(num) # Imprime 1, 2, 3
Sutileza: Muchas estructuras estándar de Python implementan el protocolo del iterador; puedes hacer que tus propios objetos sean así de fáciles.
¿Se puede usar el ciclo for-in con cualquier objeto de Python? ¿En qué se diferencia un objeto iterable de un iterador?
Respuesta: No, el ciclo for-in requiere que el objeto sea iterable, es decir, que implemente el método __iter__() que devuelve un iterador (un objeto con el método __next__()). El iterador se devuelve a sí mismo a través de __iter__(), mientras que el objeto iterable no lo hace.
Ejemplo:
lst = [1, 2, 3] iterator = iter(lst) print(hasattr(lst, '__iter__')) # True print(hasattr(iterator, '__next__')) # True (iterador) print(hasattr(lst, '__next__')) # False (no es un iterador)
Historia
Proyecto: Procesamiento de archivos grandes.
Problema: En el proyecto intentaron recorrer un objeto de archivo (iterar sobre las líneas del archivo) dos veces sin abrir explícitamente un nuevo archivo — en la segunda iteración no se obtuvo ninguna línea (¡el puntero ya estaba al final del archivo!).
Historia
Proyecto: Integración de su propia clase de colección con el marco.
Problema: La clase solo implementó
__getitem__, pero no__iter__, y no era compatible con generadores y ciclos estándar (for). Esto rompió bibliotecas externas que esperaban un iterador.
Historia
Proyecto: Uso de map/filter en colecciones personalizadas.
Problema: La propia clase de lista no implementó
__iter__y__next__, y resultó incompatible con muchas funciones estándar de Python, lo que se reveló solo después de pruebas de integración y requirió una reestructuración considerable.