【问题标题】:Why do GeneratorExit and StopIteration have different base classes?为什么 GeneratorExit 和 StopIteration 有不同的基类?
【发布时间】:2015-07-14 12:31:21
【问题描述】:

我正在查看内置 python 异常的层次结构,我注意到 StopIterationGeneratorExit 具有不同的基类:

BaseException
 +-- SystemExit
 +-- KeyboardInterrupt
 +-- GeneratorExit
 +-- Exception
      +-- StopIteration
      +-- StandardError
      +-- Warning

或者在代码中:

>>> GeneratorExit.__bases__
(<type 'exceptions.BaseException'>,)
>>> StopIteration.__bases__
(<type 'exceptions.Exception'>,)

当我转到每个异常的具体描述时,我可以阅读以下内容:

https://docs.python.org/2/library/exceptions.html#exceptions.GeneratorExit

异常生成器退出

在调用生成器的 close() 方法时引发。它直接继承自 BaseException 而不是 StandardError,因为它在技术上不是错误。

https://docs.python.org/2/library/exceptions.html#exceptions.StopIteration

异常停止迭代

由迭代器的 next() 方法引发,表示没有其他值。这是从 Exception 而不是 StandardError 派生的,因为这在其正常应用程序中不被视为错误。

这对我来说不是很清楚。两者在某种意义上是相似的,它们不通知错误,而是更改代码流的“事件”。所以,从技术上讲,它们不是错误,我知道它们应该与其他异常分开......但为什么一个是BaseException 的子类而另一个是Exception 的子类?。

一般来说,我一直认为Exception 子类是错误的,当我写一个盲目的try: except:(例如调用第三方代码)时,我总是试图捕捉Exception,但也许那是错误的,我应该会赶上StandardError

【问题讨论】:

  • 怀疑这是出于历史原因:StopIteration & Exception 早在 BaseException 之前就已经存在,更改它可能会无缘无故地破坏一些旧代码。请参阅PEP 352 异常所需的超类。请注意,StopIteration 仍然派生自 Exception in Python 3,即使这与现代模式不一致。 (我把它放在评论而不是答案中,因为这是猜测)。
  • @PM2Ring 这听起来很合乎逻辑。虽然......如果python 3仍然将StopIteration定义为Exception的子类,这可能有一个隐藏的原因
  • 相关 Python 错误跟踪链接 Issue1537:将 GeneratorExit 的基类从 Exception 更改为 BaseException

标签: python exception exception-handling python-internals


【解决方案1】:

使用 try: ... except Exception: ... 块是很常见的。

如果 GeneratorExit 从 Exception 继承,您会遇到以下问题:

def get_next_element(alist):
    for element in alist:
        try:
            yield element
        except BaseException:  # except Exception
            pass

for element in get_next_element([0,1,2,3,4,5,6,7,8,9]):
    if element == 3:
        break
    else:
        print(element)

0
1
2
Exception ignored in: <generator object get_next_element at 0x7fffed7e8360>
RuntimeError: generator ignored GeneratorExit

这个例子很简单,但是想象一下在 try 块中一个更复杂的操作,如果失败,它会简单地忽略问题(或打印一条消息)并进入下一个迭代。

如果您要捕获通用异常,您最终会阻止生成器的用户在没有收到 RuntimeError 的情况下中断循环。

更好的解释是here

编辑:在这里回答,因为评论太长了。

我宁愿说反话。 GeneratorExit 应该继承自 Exception 而不是 BaseException。当您捕获Exception 时,您基本上想捕获几乎所有内容。 BaseExceptionPEP-352 所述,适用于那些需要“排除”的异常,以允许用户逃避否则会捕获它们的代码。通过这种方式,您可以,例如,仍然 CTRL-C 运行代码。 GeneratorExit 属于该类别以打破循环。在comp.lang.python 上进行了一次有趣的对话。

【讨论】:

  • 我发现GeneratorExit 继承自BaseException 是很合乎逻辑的,但为了保持一致性,我认为StopIterator 也应该直接从BaseException 继承而不是从Exception 继承。无论如何,这不是错误。
  • @ikaros45,它现在的方式可能与BaseExceptions 的最小集合最不混淆。您不想在BaseException 异常类中添加比绝对必要的更多内容,因为这样您就有另一个可能会通过您的异常处理。因此,我同意 noxdafox 的观点,即应该从 Base 中删除 GeneratorExit,但是如果您不了解它,它会引入运行时。因此,如果您不了解它,但在 Base 类异常上仍然不太重的当前模型。
  • 可能应该有一个特殊的 ControlFlowException,它是 BaseException 的子类,并且 StopIteration 和 GeneratorExit 都应该继承自它,这样人们就可以显式地包含控制流异常,而无需同时包含 KeyboardInterrupt 和 SystemExit
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-05-10
  • 2017-11-23
  • 2022-01-22
  • 2016-07-26
  • 1970-01-01
  • 2019-07-31
相关资源
最近更新 更多