【问题标题】:Python generator, non-swallowing exception in 'coroutine'Python 生成器,“协程”中的非吞咽异常
【发布时间】:2011-04-29 04:33:57
【问题描述】:

我最近在 Python 生成器中遇到了一些令人惊讶的行为:

class YieldOne:
  def __iter__(self):
    try:
      yield 1
    except:
      print '*Excepted Successfully*'
      # raise

for i in YieldOne():
  raise Exception('test exception')

它给出了输出:

*Excepted Successfully*
Traceback (most recent call last):
  File "<stdin>", line 2, in <module>
Exception: test exception

*Excepted Successfully* 被打印出来让我(很高兴)感到惊讶,因为这是我想要的,但也很惊讶 Exception 仍然传播到顶层。我期望必须使用(在此示例中注释)raise 关键字来获得观察到的行为。

谁能解释为什么这个功能可以正常工作,以及为什么生成器中的except 没有吞下异常?

这是 Python 中唯一一个 except 不会吞下异常的实例吗?

【问题讨论】:

    标签: python exception generator


    【解决方案1】:

    您的代码没有按照您的想法执行。你不能在这样的协程中引发异常。相反,您要做的是捕获GeneratorExit 异常。看看当你使用不同的异常时会发生什么:

    class YieldOne:
      def __iter__(self):
        try:
          yield 1
        except RuntimeError:
            print "you won't see this"
        except GeneratorExit:
          print 'this is what you saw before'
          # raise
    
    for i in YieldOne():
      raise RuntimeError
    

    由于这仍然获得赞成票,以下是在生成器中引发异常的方法:

    class YieldOne:
      def __iter__(self):
        try:
          yield 1
        except Exception as e:
          print "Got a", repr(e)
          yield 2
          # raise
    
    gen = iter(YieldOne())
    
    for row in gen:
        print row # we are at `yield 1`
        print gen.throw(Exception) # throw there and go to `yield 2` 
    

    请参阅generator.throw 的文档。

    【讨论】:

    • 啊哈,现在说得通了。我最初没想到异常会“过度”传播到生成器。
    【解决方案2】:

    编辑:THC4k 所说的。

    如果您真的想在生成器中引发任意异常,请使用throw 方法:

    >>> def Gen():
    ...     try:
    ...             yield 1
    ...     except Exception:
    ...             print "Excepted."
    ...
    >>> foo = Gen()
    >>> next(foo)
    1
    >>> foo.throw(Exception())
    Excepted.
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    StopIteration
    

    您会注意到您在顶层获得了StopIteration。这些是由已用完元素的生成器产生的;它们通常被for 循环吞噬,但在这种情况下,我们让生成器引发异常,因此循环不会注意到它们。

    【讨论】:

      猜你喜欢
      • 2016-01-21
      • 2013-09-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-09-24
      • 2018-05-26
      • 1970-01-01
      • 2017-01-26
      相关资源
      最近更新 更多