Python:验证规则的用户定义异常

13 投票
5 回答
6501 浏览
提问于 2025-04-15 22:29

Python的文档中提到:

异常通常应该从Exception类派生,直接或间接地。

这里的“通常”让我有点困惑。

看看这段代码:

class good(Exception): pass
class bad(object): pass

Heaven = good()
Hell = bad()

>>> raise Heaven

Traceback (most recent call last):
  File "<pyshell#163>", line 1, in <module>
    raise Heaven
good

>>> raise Hell

Traceback (most recent call last):
  File "<pyshell#171>", line 1, in <module>
    raise Hell
TypeError: exceptions must be classes or instances, not bad

那么在阅读Python文档时,我是不是可以把“通常”理解为“总是”呢?

如果我有一个和Exception类没有关系的类层次结构,我想要“抛出”这个层次结构中的对象怎么办?

我可以随时用一个参数来抛出异常:

raise Exception, Hell

这让我觉得有点别扭。

那么,Exception(编辑:或者BaseException)类有什么特别之处,为什么只有它的“家族成员”可以被抛出呢?

5 个回答

3

通常情况下,使用“通常”这个词是因为有一些非常少见的异常类型不希望被普通的 Exception 处理器捕获。如果不确定的话,可以继承 Exception,但这个规则也有例外。

大多数异常是用来表示某种错误或特殊情况,这些情况是由代码和数据引起的。这些异常都是 Exception 的子类。如果你想抛出自己的异常,它很可能也会属于这个类别,因此也应该继承 Exception。如果你想要一个通用的异常处理器,比如用来记录错误,那么捕获 Exception 是完全合理的,这样可以通过这种方式捕获任何错误。

直接从 BaseException 继承的其他异常稍微有点不同。SystemExit 是在你调用 sys.exit() 时抛出的(或者你也可以直接抛出它)。如果你有一些顶层代码用来记录错误,那么你可能不希望它以相同的方式处理 SystemExit。以前你必须为 SystemExit 包含一个单独的处理器,以防止普通的异常处理器捕获到这个情况。

KeyboardInterrupt 代表一种意外情况,但它是由用户的外部输入引发的,所以它可以在你代码的任何地方发生;它并不依赖于代码或数据。这意味着即使你想处理它,你也可能希望以不同于从 Exception 继承的异常的方式来处理它。

22

除了 Exception 之外,还有其他有效的类可以用来继承,比如 BaseException

你可以查看 异常层次结构 的文档了解更多信息。

BaseException
 +-- SystemExit
 +-- KeyboardInterrupt
 +-- GeneratorExit
 +-- Exception
      +-- StopIteration
      +-- StandardError
      etc..

在旧版本的 Python 中,可以抛出一些除了异常以外的东西。例如在 Python 2.5 中:

>>> raise "foo"
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
foo

不过你会收到一个弃用警告:

DeprecationWarning: raising a string exception is deprecated

在新版本中,这种做法是不被允许的。你抛出的所有东西都必须是从 BaseException 继承而来的。

9

“所以在阅读Python文档时,我应该把'typically'换成''吗?”

不需要。

通常情况下,你应该从Exception这个类继承。就这么简单。这就是文档上说的。

有时候,你可能会从BaseException这个类继承。文档上没有提到这一点。你可能会选择扩展BaseException,因为你想要绕过except Exception的处理。

那...有什么特别的呢?

它们是BaseException的子类。你还需要知道什么呢?源代码是可以随时查看的。你可以阅读raise语句的源代码,看看在抛出TypeError之前,它具体检查了什么。

http://svn.python.org/view/python/trunk/Python/ceval.c?annotate=80817

查看第3456到3563行。

不过,从实际的角度来看,重要的就是“BaseException的子类”。

撰写回答