¿Por qué no puedes cambiar el comportamiento de un generador de funciones por un argumento?

Considere estas dos funciones:

def foo(): x = 0 while True: yield x x += 1 def wrap_foo(limit=10, gen=True): fg = foo() count = 0 if gen: while count < limit: yield next(fg) count += 1 else: return [next(fg) for _ in range(limit)]= 

foo() es un generador, y wrap_foo() solo pone un límite a la cantidad de datos que se generan. Estaba experimentando con que la envoltura se comportara como un generador con gen=True , o como una función regular que pone todos los datos generados en la memoria directamente con kwarg gen=False .

El comportamiento normal del generador funciona como esperaría:

 In [1352]: [_ for _ in wrap_foo(gen=True)] Out[1352]: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] 

Sin embargo, con gen=False , nada se genera.

 In [1351]: [num for num in wrap_foo(gen=False)] Out[1351]: [] 

Parece que Python preclasifica la función como un generador basado en la presencia de la statement de yield (este último ejemplo funciona perfectamente si se comenta el yield ).

¿Por qué es esto? Me gustaría entender los mecanismos en juego aquí. Estoy corriendo 3.6

Parece que Python preclasifica la función como un generador basado en la presencia de la statement de rendimiento

Sí, eso es exactamente lo que sucede. wrap_foo está determinado a ser un generador en el momento de la definición de la función. Podrías considerar usar expresiones generadoras en su lugar:

 def wrap_foo(limit=10, gen=True): fg = foo() if gen: return (next(fg) for _ in range(limit)) else: return [next(fg) for _ in range(limit)] 

Parece que Python preclasifica la función como un generador basado en la presencia de la statement de rendimiento (este último ejemplo funciona perfectamente si se comenta el rendimiento).

¿Por qué es esto?

Porque Python no puede esperar hasta que la función realmente ejecute un yield para decidir si es un generador. Primero, los generadores se definen para no ejecutar ninguno de sus códigos hasta el next . En segundo lugar, un generador nunca podría alcanzar ninguna de sus declaraciones de yield , si no genera ningún elemento.