Obtener un valor de retorno de un decorador con generador en Python.
Tengo un generador en Python. Digamos:
def gen(*args, **kwargs):
yield 1
yield 2
return 3
Ahora, quiero lo siguiente. Creo un objeto generador gen_obj = gen()
.
Luego, hago algo con él, típicamente value = next(gen)
.
y al final quiero hacer algo así:
returned_value = gen.value # debería devolver 3
Algunas respuestas para superar este problema en particular existen, pero lo complicaré más.
De hecho, no tengo un generador simple, sino un decorador:
def MakeGeneratorFromFcn(func):
@wraps(func)
def wrapped_func(*args, **kwargs):
if isgeneratorfunction(func):
gen = func(*args, **kwargs) # si es un generador, entonces incluir el yield
retval = yield from gen
return retval
return func(*args, **kwargs) # de lo contrario, esta wrapped_func ya es un generador, devuelve el api_func
return wrapped_func
Entonces, al final me gustaría hacer algo así:
@MakeGeneratorFromFcn
def foo():
return 10
gen = foo()
for i in gen:
print(i)
gen.value # devuelve 10; eso falta
y también esto funcionaría:
@MakeGeneratorFromFcn
def foo():
yield 1
return 10
gen = foo()
for i in gen:
print(i)
gen.value # devuelve 10; esto falta
EDITO #1:
Para resolver eso, utilicé esta respuesta y con algunas modificaciones:
class ValueKeepingGenerator(object):
def __init__(self, g):
self.g = g
self.value = None
def __iter__(self):
return self
def __next__(self):
self.value = next(self.g)
return self.value
Junto con otra decoración “de función a generador”, pude hacer que el código funcionara para mis propósitos.
davy.ai
Solución
El problema principal del código dado es que se pierde el valor de retorno de la función generadora cuando se llama usando un decorador. Para resolver este problema, podemos modificar la función decoradora
MakeGeneratorFromFcn
para envolver la generadora en una clase que almacene el valor de retorno de la generadora.Una forma de lograr esto es crear una nueva clase llamada
ValueKeepingGenerator
que tome una generadora como entrada y almacene su valor de retorno. Podemos modificar la funciónMakeGeneratorFromFcn
para usar esta clase y envolver la generadora.Así es como podemos modificar la clase
ValueKeepingGenerator
para almacenar el valor de retorno:Podemos modificar la función
MakeGeneratorFromFcn
para usar esta clase y envolver la generadora:Finalmente, podemos usar la función
MakeGeneratorFromFcn
modificada para decorar la funciónfoo
de la siguiente manera:Ahora, podemos usar la generadora
foo
y acceder a su valor de retorno de la siguiente manera:De manera similar, podemos modificar la línea
gen.value
en el código original agen.returned_value
.Esta solución funciona para generadoras que tienen un valor de retorno. Si una generadora no tiene un valor de retorno, entonces
gen.returned_value
seráNone
.