Historia de la cuestión: El concepto de iterabilidad surgió en Python para unificar el trabajo con colecciones: listas, diccionarios, conjuntos, etc. Cualquier objeto sobre el cual se puede iterar utilizando un bucle for se considera iterable. Esto se implementa a través de ciertos métodos mágicos.
Problema: Python requiere ciertos protocolos para el correcto funcionamiento de bucles y funciones relacionadas con secuencias. Si un usuario implementa incorrectamente estos protocolos en su clase, los mecanismos estándar (for, list(), sum(), etc.) no funcionarán o se comportarán de manera inesperada.
Solución:
Un objeto es iterable si implementa el método __iter__. Un iterador es aquel que tiene un método __next__ y __iter__ que devuelve self. Normalmente, el objeto devuelto por __iter__ es un iterador, pero no es obligatorio. Ejemplo:
class MyRange: def __init__(self, start, end): self.start = start self.end = end def __iter__(self): self.current = self.start return self def __next__(self): if self.current < self.end: val = self.current self.current += 1 return val raise StopIteration for x in MyRange(1, 4): print(x) # 1, 2, 3
Características clave:
__iter__.__next__ como __iter__ (que devuelve self).__iter__ se implementa de manera efectiva si se necesitan múltiples pases independientes sobre la colección.¿Es obligatorio el método next para cualquier objeto iterable?
No. Para un objeto iterable, solo se necesita __iter__, que devuelve un iterador. __next__ solo lo implementa el propio iterador. Por ejemplo, la lista no tiene un método __next__, pero es iterable: su __iter__ devuelve una instancia de iterador.
lst = [1, 2, 3] print(hasattr(lst, '__next__')) # False
¿Puede un objeto ser un iterador por sí mismo?
Sí, si implementa ambos métodos — __iter__ y __next__.
¿Es posible crear varios iteradores con estado independiente para una colección?
Sí, si __iter__ devuelve cada vez un nuevo objeto iterador.
class MyList: def __init__(self, data): self.data = data def __iter__(self): return iter(self.data)
__next__ sin __iter__ (o viceversa).Caso negativo: La clase del iterador almacena el estado (por ejemplo, el índice actual) a nivel de clase, no de instancia, lo que provoca que los paseos paralelos se rompan entre sí. Ventajas:
Caso positivo:
Cada iterador almacena su estado en una instancia creada en __iter__.
Ventajas: