【问题标题】:Avoid "exception ignored" in python enhanced generator在 python 增强生成器中避免“忽略异常”
【发布时间】:2013-09-09 07:55:53
【问题描述】:

我在 python 中有一个协程(增强型生成器),其中有一些代码要在数据结束后执行:

def mycoroutine():
  try:
    while True:
      data = (yield)
      print data
  finally:
    raise ValueError
    print "END"

co = mycoroutine()
co.next()

for i in (1,2,3):
  co.send(i)

ValueError 异常未引发,但解释器仅打印:

Exception ValueError: ValueError() in <generator object mycoroutine at 0x2b59dfa23d20> ignored

有没有办法在调用者中捕获异常?

【问题讨论】:

    标签: python exception generator coroutine


    【解决方案1】:

    引发了异常finally 块在生成器关闭时执行。通过在生成器上下文中引发 GeneratorExit exception 来关闭生成器。

    被忽略的异常,因为生成器在被删除之前不会关闭(在这种情况下,当 Python 退出时自动关闭);生成器__del__ 处理程序关闭生成器,从而触发finally: 块:

    >>> def mycoroutine():
    ...   try:
    ...     while True:
    ...       data = (yield)
    ...       print data
    ...   finally:
    ...     raise ValueError
    ...     print "END"
    ... 
    >>> co = mycoroutine()
    >>> co.next()
    >>> co.close()
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
      File "<stdin>", line 7, in mycoroutine
    ValueError
    >>> co = mycoroutine()
    >>> co.next()
    >>> del co
    Exception ValueError: ValueError() in <generator object mycoroutine at 0x1046a9fa0> ignored
    

    清理期间引发的异常总是被忽略;见object.__del__() documentation:

    警告:由于__del__()的危险情况 方法被调用,在它们执行期间发生的异常是 忽略,并改为向sys.stderr 打印警告。

    解决方案是在清理生成器时不引发异常,或者通过显式关闭生成器来捕获异常:

    >>> co = mycoroutine()
    >>> co.next()
    >>> try:
    ...     co.close()
    ... except ValueError:
    ...     pass
    ... 
    >>> del co
    >>> # No exception was raised
    ... 
    

    您还可以捕获GeneratorExit 异常并在此时执行一些清理:

    def mycoroutine():
      try:
        while True:
          data = (yield)
          print data
      except GeneratorExit:
        print "Generator exiting!"
    

    但请注意,除StopIterationGeneratorExit 之外的任何异常都将始终传播;见generator.close() documentation:

    如果生成器函数随后引发StopIteration(通过正常退出,或由于已被关闭)或GeneratorExit(通过未捕获异常),则关闭返回其调用者。如果生成器产生一个值,则会引发 RuntimeError。如果生成器引发任何其他异常,则会将其传播给调用者。

    【讨论】:

    • 如果我用 except GeneratorExit: 替换 finally" 也会发生同样的事情。我想它是一样的......
    • @Zac:当然可以,你仍然提出 ValueError,不是吗?捕获 GeneratorExit 并引发另一个异常不会阻止该异常传播。
    • 非常感谢该文档的链接。你让我免于数小时的调试。很奇怪,他们没有在析构函数中捕获堆栈跟踪。
    • @WalterNissen:为什么这很奇怪?当解释器退出时,就没有堆栈可追踪了。
    • 因为无法找到生成忽略异常的位置。它没有列出行甚至类,只是例外。我们有 75,000 行 Python 代码,要追踪正在执行导入的 __del__ 并因此导致 ImportError 并不容易。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-07-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多