python function generator coroutine

En Python, ¿hay alguna manera de verificar si una función es una "función generadora" antes de llamarla?



function generator (4)

Digamos que tengo dos funciones:

def foo(): return ''foo'' def bar(): yield ''bar''

El primero es una función normal, y el segundo es una función de generador. Ahora quiero escribir algo como esto:

def run(func): if is_generator_function(func): gen = func() gen.next() #... run the generator ... else: func()

¿Cómo se verá una implementación directa de is_generator_function() ? Usando el paquete de types puedo probar si gen es un generador, pero deseo hacerlo antes de invocar func() .

Ahora considere el siguiente caso:

def goo(): if False: yield else: return

Una invocación de goo() devolverá un generador. Supongo que el analizador de Python sabe que la función goo() tiene una declaración de rendimiento, y me pregunto si es posible obtener esa información fácilmente.

¡Gracias!


En realidad, me pregunto cuán útil sería realmente esa función hipotética is_generator_function() . Considerar:

def foo(): return ''foo'' def bar(): yield ''bar'' def baz(): return bar() def quux(b): if b: return foo() else: return bar()

¿Qué debería is_generator_function() devolver para baz y quux ? baz() devuelve un generador pero no es uno en sí mismo, y es posible que quux() devuelva un generador o no.


Implementé un decorador que se engancha en la función decorada devuelta / arrojó valor. Su básico va:

import types def output(notifier): def decorator(f): def wrapped(*args, **kwargs): r = f(*args, **kwargs) if type(r) is types.GeneratorType: for item in r: # do something yield item else: # do something return r return decorator

Funciona porque la función de decorador se llama de forma incondicional: es el valor de retorno que se prueba.

EDITAR: Siguiendo el comentario de Robert Lujo, terminé con algo como:

def middleman(f): def return_result(r): return r def yield_result(r): for i in r: yield i def decorator(*a, **kwa): if inspect.isgeneratorfunction(f): return yield_result(f(*a, **kwa)) else: return return_result(f(*a, **kwa)) return decorator


>>> def foo(): ... return ''foo'' ... >>> def bar(): ... yield ''bar'' ... >>> import dis >>> dis.dis(foo) 2 0 LOAD_CONST 1 (''foo'') 3 RETURN_VALUE >>> dis.dis(bar) 2 0 LOAD_CONST 1 (''bar'') 3 YIELD_VALUE 4 POP_TOP 5 LOAD_CONST 0 (None) 8 RETURN_VALUE >>>

Como puede ver, la diferencia clave es que el bytecode para la bar contendrá al menos un YIELD_VALUE operación YIELD_VALUE . Recomiendo usar el módulo dis (redireccionando su salida a una instancia de StringIO y comprobando su valor de getvalue , por supuesto) porque esto le proporciona una medida de solidez frente a los cambios en los getvalue de getvalue ; los valores numéricos exactos de los getvalue de getvalue cambiarán, pero el valor simbólico desmontado se mantendrá bastante estable ;-).


>>> import inspect >>> >>> def foo(): ... return ''foo'' ... >>> def bar(): ... yield ''bar'' ... >>> print inspect.isgeneratorfunction(foo) False >>> print inspect.isgeneratorfunction(bar) True

  • Nuevo en Python versión 2.6