在现代 Python 中声明自定义异常的正确方法?
在现代 Python 中声明自定义异常类的正确方法是什么?我的主要目标是遵循其他异常类所具有的任何标准,以便(例如)我在异常中包含的任何额外字符串都会由捕获异常的任何工具打印出来。
我所说的“现代 Python”是指可以在 Python 2.5 中运行但对于 Python 2.6 和 Python 3.* 的处理方式来说“正确”的东西。我所说的“自定义”是指一个 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 中的参数。
更新:两个答案建议重写 __init__
和 __str__
/__unicode__
/__repr__
。看起来打字很多,有必要吗?
What's the proper way to declare custom exception classes in modern Python? My primary goal is to follow whatever standard other exception classes have, so that (for instance) any extra string I include in the exception is printed out by whatever tool caught the exception.
By "modern Python" I mean something that will run in Python 2.5 but be 'correct' for the Python 2.6 and Python 3.* way of doing things. And by "custom" I mean an Exception
object that can include extra data about the cause of the error: a string, maybe also some other arbitrary object relevant to the exception.
I was tripped up by the following deprecation warning in 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
It seems crazy that BaseException
has a special meaning for attributes named message
. I gather fromPEP-352 that attribute did have a special meaning in 2.5 they're trying to deprecate away, so I guess that name (and that one alone) is now forbidden? Ugh.
I'm also fuzzily aware that Exception
has some magic parameter args
, but I've never known how to use it. Nor am I sure it's the right way to do things going forward; a lot of the discussion I found online suggested they were trying to do away with args in Python 3.
Update: two answers have suggested overriding __init__
, and __str__
/__unicode__
/__repr__
. That seems like a lot of typing, is it necessary?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(18)
也许我错过了这个问题,但为什么不呢:
要覆盖某些内容(或传递额外的参数),请执行以下操作:
这样您就可以将错误消息的字典传递给第二个参数,并稍后使用
e.errors< /代码>。
在 Python 2 中,您必须使用这种稍微复杂的
super()
形式:Maybe I missed the question, but why not:
To override something (or pass extra args), do this:
That way you could pass dict of error messages to the second param, and get to it later with
e.errors
.In Python 2, you have to use this slightly more complex form of
super()
:使用现代 Python 异常,您不需要滥用
.message
,或覆盖.__str__()
或.__repr__()
或任何它。如果您想要的只是在引发异常时显示一条信息性消息,请执行以下操作:这将给出以
MyException: My hovercraft is full of eels
结尾的回溯。如果您希望从异常中获得更大的灵活性,您可以传递一个字典作为参数:
但是,要在
except
块中获取这些详细信息有点复杂。详细信息存储在args
属性中,该属性是一个列表。您需要做这样的事情:仍然可以将多个项目传递给异常并通过元组索引访问它们,但这是高度不鼓励的(甚至打算在不久前弃用) )。如果您确实需要多条信息,并且上述方法不足以满足您的需要,那么您应该按照 教程。
With modern Python Exceptions, you don't need to abuse
.message
, or override.__str__()
or.__repr__()
or any of it. If all you want is an informative message when your exception is raised, do this:That will give a traceback ending with
MyException: My hovercraft is full of eels
.If you want more flexibility from the exception, you could pass a dictionary as the argument:
However, to get at those details in an
except
block is a bit more complicated. The details are stored in theargs
attribute, which is a list. You would need to do something like this:It is still possible to pass in multiple items to the exception and access them via tuple indexes, but this is highly discouraged (and was even intended for deprecation a while back). If you do need more than a single piece of information and the above method is not sufficient for you, then you should subclass
Exception
as described in the tutorial.这很好,除非您的异常确实是一种更具体的异常:
或者更好(也许是完美),而不是
pass
给出文档字符串:中子类化异常子类
从 文档
这意味着如果您的异常是一种更具体的异常,请对该异常进行子类化,而不是通用
Exception
(结果将是您仍然从派生>Exception
正如文档所建议的那样)。此外,您至少可以提供一个文档字符串(而不是被迫使用pass
关键字):使用自定义
__init__
设置您自己创建的属性。避免将字典作为位置参数传递,代码的未来用户会感谢你。如果您使用已弃用的消息属性,则自行分配它可以避免出现DeprecationWarning
:实际上无需编写自己的
__str__
或__repr__
。内置的非常好,并且您的合作继承确保您使用它们。对最佳答案的批评
同样,上面的问题是,为了捕获它,您要么必须专门命名它(如果在其他地方创建,则导入它)或捕获它异常,(但您可能不准备处理所有类型的异常,并且您应该只捕获准备处理的异常)。与下面的批评类似,但另外这不是通过
super
初始化的方式,如果您访问 message 属性,您将收到DeprecationWarning
:它还需要传入两个参数(除了
self。)不多也不少。这是一个有趣的限制,未来的用户可能不会意识到。
直接说 - 它违反了里氏可替换性。
我将展示这两个内容错误:
相比于:
This is fine unless your exception is really a type of a more specific exception:
Or better (maybe perfect), instead of
pass
give a docstring:Subclassing Exception Subclasses
From the docs
That means that if your exception is a type of a more specific exception, subclass that exception instead of the generic
Exception
(and the result will be that you still derive fromException
as the docs recommend). Also, you can at least provide a docstring (and not be forced to use thepass
keyword):Set attributes you create yourself with a custom
__init__
. Avoid passing a dict as a positional argument, future users of your code will thank you. If you use the deprecated message attribute, assigning it yourself will avoid aDeprecationWarning
:There's really no need to write your own
__str__
or__repr__
. The built-in ones are very nice, and your cooperative inheritance ensures that you use them.Critique of the top answer
Again, the problem with the above is that in order to catch it, you'll either have to name it specifically (importing it if created elsewhere) or catch Exception, (but you're probably not prepared to handle all types of Exceptions, and you should only catch exceptions you are prepared to handle). Similar criticism to the below, but additionally that's not the way to initialize via
super
, and you'll get aDeprecationWarning
if you access the message attribute:It also requires exactly two arguments to be passed in (aside from the
self
.) No more, no less. That's an interesting constraint that future users may not appreciate.To be direct - it violates Liskov substitutability.
I'll demonstrate both errors:
Compared to:
要正确定义您自己的异常,您应该遵循一些最佳实践:
定义一个继承自
Exception
的基类。这将允许轻松捕获与项目相关的任何异常:将异常类组织在单独的模块中(例如
exceptions.py
)通常是一个好主意。要创建特定异常,请对异常基类进行子类化。
您还可以对自定义异常类进行子类化以创建层次结构。
要向自定义异常添加对额外参数的支持,请定义一个具有可变数量参数的
__init__()
方法。调用基类的__init__()
,向其传递任何位置参数(请记住BaseException
/Exception
需要任意数量的位置参数)。将额外的关键字参数存储到实例中,例如:使用示例:
<前><代码>尝试:
raise CustomError('发生了不好的事情', custom_kwarg='value')
除了 CustomError 之外:
print(f'Сaught CustomError 异常,custom_kwarg={exc.custom_kwarg}')
此设计遵循 里氏替换原则 ,因为您可以用派生异常类的实例替换基异常类的实例。此外,它还允许您创建具有与父类相同参数的派生类的实例。
To define your own exceptions correctly, there are a few best practices that you should follow:
Define a base class inheriting from
Exception
. This will allow to easily catch any exceptions related to the project:Organizing the exception classes in a separate module (e.g.
exceptions.py
) is generally a good idea.To create a specific exception, subclass the base exception class.
You can subclass custom exception classes as well to create a hierarchy.
To add support for extra argument(s) to a custom exception, define an
__init__()
method with a variable number of arguments. Call the base class's__init__()
, passing any positional arguments to it (remember thatBaseException
/Exception
expect any number of positional arguments). Store extra keyword arguments to the instance, e.g.:Usage example:
This design adheres to the Liskov substitution principle, since you can replace an instance of a base exception class with an instance of a derived exception class. Also, it allows you to create an instance of a derived class with the same parameters as the parent.
从 Python 3.8 开始(2018 年,https://docs.python .org/dev/whatsnew/3.8.html),推荐的方法仍然是:
请不要忘记记录,为什么需要自定义异常!
如果需要,这个是使用更多数据获取异常的方法:
并像这样获取它们:
payload=None
对于使其可pickle 很重要。在转储它之前,您必须调用error.__reduce__()
。加载将按预期进行。如果您需要将大量数据传输到某些外部结构,您可能应该研究使用 python
return
语句寻找解决方案。这对我来说似乎更清晰/更Pythonic。 Java 中大量使用高级异常,当使用框架并且必须捕获所有可能的错误时,这有时会很烦人。As of Python 3.8 (2018, https://docs.python.org/dev/whatsnew/3.8.html), the recommended method is still:
Please don't forget to document, why a custom exception is neccessary!
If you need to, this is the way to go for exceptions with more data:
and fetch them like:
payload=None
is important to make it pickle-able. Before dumping it, you have to callerror.__reduce__()
. Loading will work as expected.You maybe should investigate in finding a solution using pythons
return
statement if you need much data to be transferred to some outer structure. This seems to be clearer/more pythonic to me. Advanced exceptions are heavily used in Java, which can sometimes be annoying, when using a framework and having to catch all possible errors.如果使用一个与多个属性(省略回溯),请查看默认情况下异常如何工作:
因此您可能需要一种“异常模板”,作为异常工作本身,以兼容的方式:
这可以使用这个子类轻松完成
,如果您不喜欢默认的类似元组的表示,只需将 __str__ 方法添加到 ExceptionTemplate 中类,例如:
你将会有
see how exceptions work by default if one vs more attributes are used (tracebacks omitted):
so you might want to have a sort of "exception template", working as an exception itself, in a compatible way:
this can be done easily with this subclass
and if you don't like that default tuple-like representation, just add
__str__
method to theExceptionTemplate
class, like:and you'll have
您应该重写 __repr__ 或 __unicode__ 方法而不是使用消息,构造异常时提供的参数将位于该异常的 args 属性中异常对象。
You should override
__repr__
or__unicode__
methods instead of using message, the args you provide when you construct the exception will be in theargs
attribute of the exception object.请参阅一篇非常好的文章“ Python 异常的权威指南”。基本原则是:
BaseException.__init__
。还有有关组织(在模块中)和包装异常的信息,我建议阅读该指南。
See a very good article "The definitive guide to Python exceptions". The basic principles are:
BaseException.__init__
with only one argument.There is also information on organizing (in modules) and wrapping exceptions, I recommend to read the guide.
不,“消息”并不被禁止。它刚刚被弃用。您的应用程序将与使用消息一起正常工作。但当然,您可能希望摆脱弃用错误。
当您为应用程序创建自定义 Exception 类时,其中许多类不仅仅从 Exception 子类化,而是从其他子类子类化,例如
ValueError
或类似的子类。然后你必须适应他们对变量的使用。如果您的应用程序中有许多异常,那么通常最好为所有异常使用一个通用的自定义基类,以便模块的用户可以执行此操作
,在这种情况下,您可以执行
__init__
和那里需要__str__
,因此您不必为每个异常重复它。但简单地调用消息变量而不是消息就可以了。无论如何,如果您执行与 Exception 本身不同的操作,则只需要
__init__
或__str__
。因为如果弃用,那么您就需要两者,否则您会收到错误。每个类并不需要太多额外的代码。No, "message" is not forbidden. It's just deprecated. You application will work fine with using message. But you may want to get rid of the deprecation error, of course.
When you create custom Exception classes for your application, many of them do not subclass just from Exception, but from others, like
ValueError
or similar. Then you have to adapt to their usage of variables.And if you have many exceptions in your application it's usually a good idea to have a common custom base class for all of them, so that users of your modules can do
And in that case you can do
__init__
and__str__
needed there, so you don't have to repeat it for every exception. But simply calling the message variable something else than message does the trick.In any case, you only need
__init__
or__str__
if you do something different from what Exception itself does. And because if the deprecation, you then need both, or you get an error. That's not a whole lot of extra code you need per class.为了最大程度地定制,要定义自定义错误,您可能需要定义一个继承自 Exception 类的中间类,如下所示:
For maximum customisation, to define custom errors, you may want to define an intermediate class that inherits from
Exception
class as:从 Python 3.9.5 开始,我对上述方法遇到了问题。
但是,我发现这对我有用:
然后它可以在如下代码中使用:
I had issues with the above methods, as of Python 3.9.5.
However, I found that this works for me:
And then it could be used in code like:
可以使用
dataclass
来简化自定义异常的定义:这减少了一些样板文件,同时保持了进一步自定义的灵活性。
It's possible to use
dataclass
to simplify the definition of the custom exception:This reduces some of the boilerplate, while remaining flexible for further customization.
一个非常简单的方法:
或者,在不打印
__main__
的情况下引发错误(可能看起来更干净整洁):A really simple approach:
Or, have the error raise without printing
__main__
(may look cleaner and neater):试试这个例子
Try this Example
我遇到了这个线程。这就是我处理自定义异常的方法。虽然
Fault
类稍微复杂,但它使得使用变量参数声明自定义表达异常变得微不足道。FinalViolation
、SingletonViolation
都是TypeError
的子类,因此将在下面的代码中捕获。这就是
Fault
不继承自Exception
的原因。允许派生异常继承自其选择的异常。遗憾的是,
FinalViolation
、SingletonViolation
只接受 1 个参数。但人们很容易造成多参数错误,例如
I came across this thread. This is how I do custom exceptions. While the
Fault
class is slightly complex, it makes declaring custom expressive exceptions with variable arguments trivial.FinalViolation
,SingletonViolation
are both sub classes ofTypeError
so will be caught code below.That's why
Fault
doesn't inherit fromException
. To allow derivative exceptions to inherit from the exception of their choice.FinalViolation
,SingletonViolation
unfortunately only accept 1 argument.But one could easily create a multi arg error e.g.
同意@sultanorazbayev的回答。为了简单起见应该使用数据类
Agreed with @sultanorazbayev's answer. Should use dataclasses for simplicity
几个答案都有很好的观点,但我仍在努力清楚地了解 v3.13 中 Python 推荐的自定义异常方法。我还认为其他答案主要涉及该方法的各个方面,而没有对实现和用法的完整描述。
冒着让这个问题变得拥挤的风险,我将我当前的方法(使用 Python v3.10)与使用场景结合起来,试图整理最新的建议。
此示例具有适合 REST API 实现的特征,但可以改变用途。
据我所知,推荐的原则是:
Exception
str
参数调用super().__init__
__str__
控制异常的显示此示例不处理子异常的附加参数。对于这个遗漏,我深表歉意。该示例确实支持其他类属性,这些属性无需传递参数即可提供详细信息。
在使用中,我有时会将内置异常转换为自定义异常,以实现处理的一致性。
exceptions.py:
为了说明一些常见用法,将应用程序代码包装在
try
块中并处理来自不同来源的各种错误,此示例具有单个 catch 序列和所有路径始终以status
变量引用的定义APIException
实例退出。这也可以是成功途径的exceptions.OK
。然后,status
始终包含带有int
状态代码的status.code
和用于响应描述的status.message
,其中成功响应大多会显示“OK”。handler.py:
注意如何在类中使用
APIException
location
静态方法从其他异常返回统一字符串:There are good points in several answers but I'm still working on getting a clear understanding of what the Python recommended approach to customised exceptions is in v3.13. I also feel the other answers mostly address aspects of the approach without a full description of implementation and usage.
At the risk of crowding this question I'm putting my current approach (using Python v3.10) with a usage scenario to attempt to collate the most recent advice.
This example has characteristics that suit a REST API implementation but can be repurposed.
The recommended principles as far as I can determine are:
Exception
super().__init__
with onestr
argument__str__
to control display of the exceptionsThis example does not handle additional parameters to the child exceptions. Sorry for this omission. The example does support additional class attributes which provide detail without needing to pass parameters.
In usage I sometimes convert built-in exceptions to custom exceptions for uniformity of processing.
exceptions.py:
To illustrate some common usage, wrapping application code in a
try
block and handling a variety of errors from different sources, this example has a single catch sequence and all pathways always exit with a definingAPIException
instance referred to by thestatus
variable. This can also beexceptions.OK
for the success pathway.status
then always containsstatus.code
with anint
status code andstatus.message
for the response description, which will mostly be"OK"
for successful responses.handler.py:
Notice how the
APIException
location
static method can be used from the class to return a uniform string from other exceptions:对我来说,它只是 __init__ 和变量,但有时会进行测试。
我的样本:
输出:
For me it is just
__init__
and variables but making sometimes testing.My sample:
Output: