Los generadores son una herramienta sencilla pero poderosa de Python que te permite crear iteradores de una manera más rápida y eficiente. Un iterador es un objeto que se puede iterar (o recorrer), como una lista, un tuple, un diccionario, etc.
Un generador se crea con la palabra clave `yield` en lugar de `return` en una función. Aquí hay un ejemplo simple:
```python
def my_generator():
yield 1
yield 2
yield 3
for i in my_generator():
print(i)
```
Esta es la salida:
```
1
2
3
```
Aquí está lo que sucede en el fondo:
1. Cuando llamas a la función, genera un objeto generador, pero no ejecuta nada dentro de la función.
2. Cuando iteras el objeto generador (por ejemplo, utilizando un bucle `for`), Python comienza a ejecutar el cuerpo de la función hasta que encuentra la palabra clave `yield`. En ese punto, devuelve el valor actual de `yield`, pausa la ejecución de la función y mantiene el estado actual de todas las variables locales.
3. En cada subsecuente iteración, Python reanuda la ejecución de la función desde donde lo dejó, de nuevo ejecutando hasta que encuentra la palabra clave `yield` y devuelve su valor actual.
Este proceso continúa hasta que se sale de la función. En ese caso, si intentas hacer una iteración adicional en el objeto generador, Python lanzará una excepción `StopIteration` para señalar que todos los valores han sido generados.
La principal ventaja de los generadores es que permiten controlar el flujo de grandes conjuntos de datos sin la necesidad de tenerlos todos cargados en memoria a la vez. Esto hace que los generadores sean especialmente útiles para trabajar con cantidades de datos que son demasiado grandes para caber en la memoria.