我尝试定义一个生成器函数mycount()
,可以使用生成器函数send(0)
重置,如下例所示。一切正常,除非我在尚未启动的新生成器对象上使用send(0)
。在这种情况下,它会给出TypeError
。是否有任何函数检查生成器是否已启动,或者是否必须捕获TypeError
并在这种情况下使用mycount(0)
创建新的生成器对象?
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
答案 0 :(得分:8)
为避免向刚刚启动的生成器发送非None
值,您需要先调用next
或send(None)
。我同意其他人David Beazley的coroutine decorator(在python 3.x中你需要调用__next__()
函数而不是next()
)是一个很好的选择。虽然那个特殊的装饰器很简单,但我也成功地使用了copipes库,这是Beazley演示中许多实用程序的很好实现,包括协程。
关于是否可以检查生成器是否已启动 - 在Python 3中,您可以使用inspect.getgeneratorstate。这在Python 2中不可用,但是CPython implementation是纯Python,并且不依赖于Python 3的任何新东西,所以你可以用同样的方式检查自己:
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
具体而言,如果g2
启动inspect.getgeneratorstate(g2) != inspect.GEN_CREATED
。
答案 1 :(得分:1)
由于您的错误意味着必须在刚刚启动的生成器上使用send
调用None
函数
(docs-link)。
你可以抓住TypeError
并从那里滚动:
#...
try:
g2.send(0)
except TypeError:
#Now you know it hasn't started, etc.
g2.send(None)
无论哪种方式它都不能用于“重置”发生器,它只需要重新制作。
生成器概念和语法here的精彩概述,涵盖了生成器和其他高级主题的链接。
答案 2 :(得分:1)
特别是,您可能会找到一种方法来使用第2页中描述的consumer
装饰器。大卫比兹利的“发电机技巧”I-131,J. Gwyn提供了一个链接:
def consumer(func):
def start(*args,**kwargs):
c = func(*args,**kwargs)
c.next()
return c
return start
我在代码中使用类似的东西。
请注意,if v is None
优先于if v == None
。
答案 3 :(得分:0)
这是一个完整的Python2兼容程序实现,getgeneratorstate(gtor),带有测试代码。
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)