Patrón de Iterador en Python
Imagina que tienes un frasco lleno de caramelos de varios colores. Quieres probar los caramelos uno a uno, pero sin tener que vaciar todos los caramelos de una vez. En ese momento, metes la mano en el frasco y sacas un caramelo a la vez: ese proceso es la iteración.
En programación,el patrón de iteradores un patrón de diseño que proporciona una forma de acceder secuencialmente a los elementos de un objeto de colección sin necesidad de exponer la representación interna de dicho objeto.
Conceptos clave
El patrón de iterador consta de dos componentes principales:
- Iterador (Iterator): responsable de definir la interfaz para acceder y recorrer los elementos
- Objeto iterable (Iterable): proporciona el método para crear iteradores
En Python, el patrón de iterador está profundamente integrado en las características principales del lenguaje, lo que hace que nuestra programación sea más elegante e intuitiva.
¿Por qué se necesitan iteradores?
Limitaciones del enfoque tradicional
Veamos primero un ejemplo sin usar iteradores:
Ejemplo
class BookCollection:
def __init__(self):
self.books = ["Introducción a Python", "Introducción a los algoritmos", "Patrones de diseño", "Estructuras de datos"]
def get_books(self):
return self.books
# Usar esta colección
collection = BookCollection()
books = collection.get_books()
# Iterar sobre los libros - expone detalles de implementación interna
for i in range(len(books)):
print(books[i])
Análisis del problema:
- El código del cliente necesita conocer la estructura interna de la colección (lista).
- Si la implementación interna de la colección cambia (por ejemplo, de lista a diccionario), todo el código del cliente debe modificarse.
- La lógica de recorrido está fuertemente acoplada a la implementación de la colección.
Ventajas del iterador
Ejemplo
for book in collection:
print(book)
Beneficios que aporta el iterador:
- Encapsulamiento: oculta la implementación interna de la colección.
- Interfaz unificada: diferentes tipos de colecciones pueden usar la misma forma de recorrido.
- Flexibilidad: se puede cambiar fácilmente la implementación de la colección.
- Soporte de múltiples recorridos: se pueden realizar varias operaciones de recorrido simultáneamente.
El protocolo del iterador en Python
Python implementa el protocolo del iterador mediante dos métodos especiales:
__iter__()Método
- Devuelve un objeto iterador.
- Los objetos iterables deben implementar este método.
__next__()Método
- Devuelve el siguiente elemento de la secuencia.
- Cuando no hay más elementos, lanza una
StopIterationexcepción.
Entendamos el mecanismo de funcionamiento del iterador mediante un diagrama de flujo:

Crear un iterador personalizado
Método 1: Implementar el iterador usando una clase
Vamos a crear un iterador personalizado de libros:
Ejemplo
"""Clase iterador de libros"""
def __init__(self, books):
self.books = books
self.index = 0
def __iter__(self):
"""Devuelve el propio iterador"""
return self
def __next__(self):
"""Devuelve el siguiente libro, o lanza StopIteration si no hay más libros"""
if self.index < len(self.books):
book = self.books[self.index]
self.index += 1
return book
else:
raise StopIteration
class BookCollection:
"""Clase de colección de libros iterable"""
def __init__(self):
self.books = ["Introducción a Python", "Introducción a los algoritmos", "Patrones de diseño", "Estructuras de datos"]
def __iter__(self):
"""Devuelve una instancia de iterador"""
return BookIterator(self.books)
# Usar un iterador personalizado
collection = BookCollection()
for book in collection:
print(f"Leyendo actualmente: {book}")
Resultado de salida:
正在阅读: Python入门 正在阅读: 算法导论 正在阅读: 设计模式 正在阅读: 数据结构
Método 2: Usar funciones generadoras
Python ofrece una forma más concisa: las funciones generadoras.
Ejemplo
def __init__(self):
self.books = ["Introducción a los algoritmos", "Patrones de diseño", "Estructuras de datos", """Crear un iterador con una función generadora"""]
def __iter__(self):
# La forma de uso es exactamente la misma
for book in self.books:
yield book
"Leyendo: {book}"
collection = BookCollection()
for book in collection:
print(fVentajas de los generadores:)
Código más conciso
- Manejo automático del estado guardado
- Mejor rendimiento
- Escenarios de aplicación práctica del iterador
Escenarios de aplicación práctica del iterador
Escenario 1: Lectura de datos paginados
Ejemplo
# Simular la lectura de una página de datos de la base de datos
def __init__(self, total_items, page_size=3):
self.total_items = total_items
self.page_size = page_size
self.current_page = 0
def __iter__(self):
return self
def __next__(self):
start = self.current_page * self.page_size
end = start + self.page_size
if start >= self.total_items:
raise StopIteration
# Usar el iterador de paginación
page_data = list(range(start, min(end, self.total_items)))
self.current_page += 1
return page_data
# En total 10 datos, 3 por página
paginator = PaginatedData(10, 3) "Datos de la página {page_num}: {page_data}"
for page_num, page_data in enumerate(paginator, 1):
print(fResultado de salida:)
Escenario 2: Generación de secuencias infinitas
第1页数据: [0, 1, 2] 第2页数据: [3, 4, 5] 第3页数据: [6, 7, 8] 第4页数据: [9]
Escenario 2: Generación de secuencias infinitas
Ejemplo
# Generar la secuencia de Fibonacci
def __init__(self, max_count=10):
self.max_count = max_count
self.count = 0
self.a, self.b = 0, 1
def __iter__(self):
return self
def __next__(self):
if self.count >= self.max_count:
raise StopIteration
result = self.a
self.a, self.b = self.b, self.a + self.b
self.count += 1
return result
"Secuencia de Fibonacci:"
fib = FibonacciIterator(8)
print(Resultado de salida:, list(fib))
Herramientas integradas para iteradores
斐波那契数列: [0, 1, 1, 2, 3, 5, 8, 13]
Herramientas integradas para iteradores
Función
iter()ynext()Función
Ejemplo
# Salida: 1
iterator = iter(numbers)
print(next(iterator)) # Salida: 2
print(next(iterator)) # Salida: 3
print(next(iterator)) Función
enumerate()Función
Ejemplo
for index, fruit in enumerate(fruits):
print(fFunción)
zip()Función
Ejemplo
scores = [85, 92, 78]
for name, score in zip(names, scores):
print(fIterador vs Objeto iterable)
Iterador vs Objeto iterable
Característica
| Objeto iterable (Iterable) | Iterador (Iterator) | Definición |
|---|---|---|
| Implementa | el método__iter__()Implementa |
el método__iter__()y__next__()Uso |
| Puede ser iterado | Ejecuta realmente la operación de iteración | Estado |
| Generalmente sin estado | Mantiene el estado de iteración (posición actual, etc.) | Ejemplo |
| Listas, tuplas, diccionarios, cadenas | Objeto devuelto por | iter()Diagrama de relación |
Diagrama de relación
Ejemplo
B -->|llamar a next repetidamente| C[elemento por elemento]
C --> D[hasta StopIteration]
Mejores prácticas y errores comunes
Mejores prácticas y errores comunes
Mejores prácticas
- Ejemplo
Ejemplo
def countdown(n):
while n > 0:
yield n
n -= 1
Aprovechar las funciones integradas
- Ejemplo
Ejemplo
squares = (x*x for x in range(10)) # No recomendado
# ... implementación extensa
class SquareIterator:
Errores comunes
Errores comunes
Ejemplo
Ejemplo
# Error: la lista en sí no es un iterador
try:
next(numbers) # TypeError: 'list' object is not an iterator
except TypeError as e:
print(f"Error: {e}")
# Correcto: primero obtener el iterador
iterator = iter(numbers)
print(next(iterator)) # Salida: 1
Error 2: continuar usando el iterador después de agotarse
Ejemplo
iterator = iter(numbers)
print(list(iterator)) # Salida: [1, 2, 3]
print(list(iterator)) # Salida: [] - ¡El iterador está agotado!
Ejercicios prácticos
Ejercicio 1: Crear un iterador personalizado
Crear unaCountdownclase que implemente un iterador que cuente hacia atrás desde un número especificado hasta 1:
Ejemplo
def __init__(self, start):
self.start = start
def __iter__(self):
# Tu código aquí
current = self.start
while current > 0:
yield current
current -= 1
# Prueba tu implementación
for num in Countdown(5):
print(num) # Debería mostrar: 5, 4, 3, 2, 1
Ejercicio 2: Iterador de líneas de archivo
Crear un iterador que pueda leer un archivo línea por línea y añada el número de línea al inicio de cada línea:
Ejemplo
def __init__(self, filename):
self.filename = filename
def __iter__(self):
with open(self.filename, 'r', encoding='utf-8') as file:
for line_num, line in enumerate(file, 1):
yield f"{line_num}: {line.rstrip()}"
Resumen
El patrón iterador es un concepto extremadamente importante en la programación Python; nos permite:
- Acceso unificado: recorrer diferentes estructuras de datos de la misma manera
- Encapsulación de la implementación: ocultar la estructura interna de la colección
- Evaluación perezosa: generar datos solo cuando sea necesario, ahorrando memoria
- Uso combinado: se integra perfectamente con características como generadores, comprensiones, etc.
Recuerda este simple principio:Cualquier objeto que implemente__iter__()el método es iterable, y cualquier objeto que implemente__next__()el método es iterador.