¿Existe una función de Python que verifique si se inicia un generador?

Intento definir una función del generador mycount() que se puede restablecer con el send(0) función del generador send(0) como se muestra en el siguiente ejemplo. Todo funciona bien, excepto cuando uso send(0) en un nuevo objeto generador que aún no se ha iniciado. En este caso da un TypeError . ¿Hay alguna función que verifique si el generador se ha iniciado o tengo que capturar el TypeError y crear un nuevo objeto generador con mycount(0) en tal caso?

 def mycount(value): while True: v = yield value if v == None: value = value + 1 else: value = v g = mycount(3) print(next(g)) # prints 3 print(next(g)) # prints 4 print(g.send(0)) # prints 0 print(next(g)) # prints 1 print(next(g)) # prints 2 g2 = mycount(3) g2.send(0) # TypeError: can't send non-None value to a just-started generator 

Para evitar enviar un valor que no sea None a un generador recién iniciado, primero debe llamar al next o send(None) . Estoy de acuerdo con los demás en que el decorador de coroutine de David Beazley (en python 3.x debe llamar a la función __next__() lugar de a next() ) es una excelente opción. Aunque ese decorador en particular es simple, también he usado con éxito la biblioteca de copipes , que es una buena implementación de muchas de las utilidades de las presentaciones de Beazley, incluida la coroutine.

Con respecto a si se puede verificar si se inicia un generador: en Python 3, puede usar inspect.getgeneratorstate . Esto no está disponible en Python 2, pero la implementación de CPython es pura de python y no se basa en nada nuevo para Python 3, por lo que puede comprobarse de la misma manera:

 if generator.gi_running: return GEN_RUNNING if generator.gi_frame is None: return GEN_CLOSED if generator.gi_frame.f_lasti == -1: return GEN_CREATED return GEN_SUSPENDED 

Específicamente, g2 se inicia si inspect.getgeneratorstate(g2) != inspect.GEN_CREATED .

Como su error implica que la función de send debe llamarse con None en un generador recién iniciado (docs-link) .

Podrías atrapar el TypeError y rodar desde allí:

  #... try: g2.send(0) except TypeError: #Now you know it hasn't started, etc. g2.send(None) 

De cualquier manera, no se puede utilizar para “reiniciar” el generador, solo se debe rehacer.

Gran descripción general de los conceptos y la syntax del generador aquí , que cubre el encadenamiento de generadores y otros temas avanzados.

En particular, puede encontrar una manera de usar el decorador de consumer descrito en la p. I-131 de “Generator Tricks” de David Beazley, a la que J. Gwyn proporcionó un enlace:

 def consumer(func): def start(*args,**kwargs): c = func(*args,**kwargs) c.next() return c return start 

Yo uso algo similar en mi código.

Tenga en cuenta que if v is None se prefiere más que if v == None .

Aquí hay una implementación completa de rutina compatible con Python2, getgeneratorstate (gtor), con código de prueba.

 import unittest import enum class GtorState(enum.Enum): GEN_RUNNING ='GEN_RUNNING' GEN_CLOSED ='GEN_CLOSED' GEN_CREATED ='GEN_CREATED' GEN_SUSPENDED ='GEN_SUSPENDED' @staticmethod def getgeneratorstate(gtor): if gtor.gi_running: return GtorState.GEN_RUNNING if gtor.gi_frame is None: return GtorState.GEN_CLOSED if gtor.gi_frame.f_lasti == -1: return GtorState.GEN_CREATED return GtorState.GEN_SUSPENDED #end-def def coro000(): """ a coroutine that does little """ print('-> coroutine started') x =yield print('-> coroutine received ', x) class Test_Coro(unittest.TestCase): def test_coro000(self): my_coro000 =coro000() self.assertEqual( GtorState.getgeneratorstate(my_coro000), GtorState.GEN_CREATED) next(my_coro000) # prints '-> coroutine started' self.assertEqual( GtorState.getgeneratorstate(my_coro000), GtorState.GEN_SUSPENDED) try: my_coro000.send(42) # prints '-> coroutine received 42 self.assertEqual( GtorState.getgeneratorstate(my_coro000), GtorState.GEN_SUSPENDED) self.fail('should have raised StopIteration ') except StopIteration: self.assertTrue(True, 'On exit a coroutine will throw StopIteration') self.assertEqual( GtorState.getgeneratorstate(my_coro000), GtorState.GEN_CLOSED)