【问题标题】:Is there a Python function that checks if a generator is started?是否有一个 Python 函数可以检查生成器是否已启动?
【发布时间】:2013-07-15 03:03:59
【问题描述】:

我尝试定义一个生成器函数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

【问题讨论】:

    标签: python generator


    【解决方案1】:

    为避免向刚启动的生成器发送非None 值,您需要先调用nextsend(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
    

    具体来说,如果inspect.getgeneratorstate(g2) != inspect.GEN_CREATED,则启动g2

    【讨论】:

      【解决方案2】:

      由于您的错误暗示必须在刚刚启动的生成器上使用 None 调用 send 函数 (docs-link)

      可以抓住TypeError并从那里滚动:

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

      无论哪种方式,它都不能用来“重置”生成器,只能重新制作。

      生成器概念和语法here 的精彩概述,涵盖生成器链接和其他高级主题。

      【讨论】:

        【解决方案3】:

        特别是,您可能会找到一种方法来使用第 1 页中描述的 consumer 装饰器。 David Beazley 的“发电机技巧”中的 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

        【讨论】:

        • 这确实是一个不错的解决方案,但它仅适用于使用此装饰器的生成器函数。我更喜欢适用于每台发电机的东西。所以,我使用 Rich Franks 解决方案。
        • 没错,这个解决方案是有限的。更重要的 IMO 是它改变了生成器的行为。
        【解决方案4】:

        这是一个完整的实现 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)
        

        【讨论】:

          猜你喜欢
          • 2013-05-21
          • 2016-06-30
          • 1970-01-01
          • 1970-01-01
          • 2010-12-24
          • 1970-01-01
          • 2010-12-29
          • 2015-08-24
          相关资源
          最近更新 更多