在现代Python中声明自定义异常类的正确方法是什么?我的主要目标是遵循标准的其他异常类,以便(例如)我在异常中包含的任何额外字符串由捕获异常的任何工具打印出来 .
通过“现代Python”,我的意思是在Python 2.5中运行,但对于Python 2.6和Python 3 *做事的方式是“正确的” . 而“custom”我指的是一个Exception对象,它可以包含有关错误原因的额外数据:一个字符串,也许还有一些与异常相关的任意对象 .
我在Python 2.6.2中被以下弃用警告绊倒了:
>>> class MyError(Exception):
... def __init__(self, message):
... self.message = message
...
>>> MyError("foo")
_sandbox.py:3: DeprecationWarning: BaseException.message has been deprecated as of Python 2.6
BaseException
对于名为 message
的属性具有特殊含义,这似乎很疯狂 . 我从PEP-352收集了这个属性确实在2.5中有特殊意义他们试图弃用,所以我猜这个名字(而且仅此一个)现在被禁止了?啊 .
我也模糊地意识到 Exception
有一些神奇的参数 args
,但我是正确的做事方式 . 我在网上发现的很多讨论都表明他们试图在Python 3中废除args .
更新:建议覆盖 __init__
和 __str__
/ __unicode__
/ __repr__
两个答案 . 这似乎很多打字,是否有必要?
8 回答
如果使用一个或多个属性(省略回溯),请查看默认情况下异常如何工作:
所以你可能希望有一种“ exception template ”,以兼容的方式作为例外本身:
这可以通过这个子类轻松完成
如果你不喜欢那个默认的类似于元组的表示,只需将
__str__
方法添加到ExceptionTemplate
类,如:你会的
也许我错过了这个问题,但为什么不呢:
Edit: 覆盖某些东西(或传递额外的args),执行以下操作:
这样你就可以将错误消息的dict传递给第二个参数,然后用
e.errors
来获取它 .Python 3 Update: 在Python 3中,您可以使用
super()
稍微更紧凑的使用:使用现代Python异常,您不需要滥用
.message
,或覆盖.__str__()
或.__repr__()
或其中任何一个 . 如果你想要的只是提出异常的信息,请执行以下操作:这将以
MyException: My hovercraft is full of eels
结束回溯 .如果您希望异常具有更大的灵活性,可以将字典作为参数传递:
但是,要在
except
块中获取这些细节有点复杂 . 详细信息存储在args
属性中,该属性是一个列表 . 你需要做这样的事情:仍然可以将多个项目传递给异常并通过元组索引访问它们,但这是非常不鼓励的(甚至打算暂时弃用) . 如果您确实需要多条信息并且上述方法对您来说还不够,那么您应该按照tutorial中的描述继承
Exception
.您应该覆盖
__repr__
或__unicode__
方法而不是使用消息,在构造异常时提供的参数将位于异常对象的args
属性中 .这很好,除非您的异常实际上是一种更具体的异常:
或者更好(也许是完美的),而不是
pass
给出一个文档字符串:子类化异常子类
来自docs
这意味着 if 您的异常是一种更具体的异常,子类是异常而不是泛型
Exception
(结果将是您仍然从文档推荐的Exception
派生) . 此外,您至少可以提供docstring(而不是强制使用pass
关键字):使用自定义
__init__
设置您自己创建的属性 . 避免传递dict作为位置参数,未来的代码用户会感谢你 . 如果您使用已弃用的消息属性,则自行分配将避免DeprecationWarning
:真的没有必要编写自己的
__str__
或__repr__
. 内置的非常好,你的 cooperative inheritance 确保你使用它 .批评最佳答案
同样,上面的问题是,为了捕获它,你可能没有准备好处理所有类型的异常,你应该只捕获你准备处理的异常) . 类似的批评下面,但另外这不是通过
super
初始化的方式,如果你访问消息属性,你将获得DeprecationWarning
:它还需要传递两个参数(除了
self
之外) . 不多也不少 . 这是未来用户可能不会欣赏的有趣约束 .To be direct - it violates Liskov substitutability.
我将演示这两个错误:
相比:
As of Python 3.8 (2018,https://docs.python.org/dev/whatsnew/3.8.html),推荐的方法仍然是:
Please don't forget to document, why a custom exception is neccessary!
如果需要,这是获取更多数据的异常的方法:
并获取它们像:
payload=None
对于使其成为可选择的重要 . 在转储它之前,你必须调用error.__reduce()__
. 加载将按预期工作 .如果需要将大量数据传输到某个外部结构,您可能应该使用pythons
return
语句查找解决方案 . 对我来说,这似乎更清晰/更pythonic . 高级异常在Java中被大量使用,当使用框架并且必须捕获所有可能的错误时,这有时会令人讨厌 .试试这个例子
不,“消息”不被禁止 . 它刚刚被弃用了 . 您的应用程序将使用消息正常工作 . 但是,当然,您可能希望摆脱弃用错误 .
为应用程序创建自定义异常类时,其中许多不仅仅是从Exception继承,而是从其他类(如ValueError或类似的)继承 . 然后你必须适应他们对变量的使用 .
如果你的应用程序中有很多例外,那么为所有这些例子设置一个通用的自定义基类通常是个好主意,这样你的模块用户就可以做了
在这种情况下,您可以在那里执行
__init__ and __str__
,因此您不必为每个例外重复它 . 但是简单地调用消息变量而不是消息就可以了 .在任何情况下,如果您执行与Exception本身不同的操作,则只需要
__init__ or __str__
. 因为如果弃用,则需要两者,否则会出错 . 这并不是每个课程所需的额外代码 . ;)