在现代Python中声明自定义异常类的正确方法是什么?我的主要目标是遵循其他异常类所具有的任何标准,以便(例如)我在异常中包含的任何额外字符串都由捕捉到异常的任何工具打印出来。
我所说的“现代Python”是指在Python2.5中运行,但对于Python2.6和Python3.*的工作方式来说是“正确的”。我所说的“自定义”是指一个异常对象,它可以包含有关错误原因的额外数据:一个字符串,也可能是与异常相关的其他任意对象。
我被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有一些神奇的参数参数,但我从未知道如何使用它;我在网上发现的许多讨论都表明,他们试图在Python 3中取消args。
更新:两个答案建议重写__init__和__str__/__unicode__/__repr_。这似乎需要很多打字,是吗?
不,不禁止“消息”。它刚刚被弃用。您的应用程序可以使用message正常工作。当然,您可能希望消除弃用错误。
当您为应用程序创建自定义的Exception类时,其中许多类不仅仅是从Exception派生的,而是从其他类派生的,比如ValueError或类似的类。然后你必须适应他们对变量的使用。
如果应用程序中有许多异常,通常最好为所有异常设置一个通用的自定义基类,这样模块的用户就可以
try:
...
except NelsonsExceptions:
...
在这种情况下,您可以在那里执行所需的__init__和__str__,因此不必对每个异常重复执行。但是,简单地调用消息变量而不是消息就行了。
在任何情况下,您只需要__init__或__str__,如果您执行了与Exception本身不同的操作。因为如果你不赞成,你就需要两者,否则你会得到一个错误。这不是每个类需要的大量额外代码。
我偶然发现了这条线索。这就是我如何处理自定义异常。虽然Fault类有点复杂,但它使用变量参数声明自定义表达异常变得微不足道。
FinalVibration和SingletonVibration都是TypeError的子类,因此将在下面捕获代码。
try:
<do something>
except TypeError as ex:
<handler>
这就是Fault不能继承自Exception的原因。允许派生异常从其选择的异常继承。
class Fault:
"""Generic Exception base class. Note not descendant of Exception
Inheriting exceptions override formats"""
formats = '' # to be overriden in descendant classes
def __init__(self, *args):
"""Just save args for __str__"""
self.args = args
def __str__(self):
"""Use formats declared in descendant classes, and saved args to build exception text"""
return self.formats.format(*self.args)
class TypeFault(Fault, TypeError):
"""Helper class mixing Fault and TypeError"""
class FinalViolation(TypeFault):
"""Custom exception raised if inheriting from 'final' class"""
formats = "type {} is not an acceptable base type. It cannot be inherited from."
class SingletonViolation(TypeFault):
"""Custom exception raised if instancing 'singleton' class a second time"""
formats = "type {} is a singleton. It can only be instanced once."
FinalViolation,SingletonViolation不幸只接受1个参数。
但是可以很容易地创建多参数错误,例如。
class VesselLoadingError(Fault, BufferError):
formats = "My {} is full of {}."
raise VesselLoadingError('hovercraft', 'eels')
__main__.船只装载错误:我的气垫船上满是鳗鱼。
使用现代Python异常,您不需要滥用.message或重写__str__()或__repr_()或其中任何一个。如果在引发异常时只需要一条信息性消息,请执行以下操作:
class MyException(Exception):
pass
raise MyException("My hovercraft is full of eels")
这将以MyException结尾:我的气垫船上满是鳗鱼。
如果希望从异常中获得更多的灵活性,可以传递字典作为参数:
raise MyException({"message":"My hovercraft is full of animals", "animal":"eels"})
然而,在except块中获取这些细节有点复杂。详细信息存储在args属性中,该属性是一个列表。你需要这样做:
try:
raise MyException({"message":"My hovercraft is full of animals", "animal":"eels"})
except MyException as e:
details = e.args[0]
print(details["animal"])
仍然可以将多个项传递给异常,并通过元组索引访问它们,但这是非常不鼓励的(甚至在不久前还打算弃用)。如果您确实需要一条以上的信息,并且上面的方法对您来说还不够,那么您应该按照教程中的描述对Exception进行子类化。
class MyError(Exception):
def __init__(self, message, animal):
self.message = message
self.animal = animal
def __str__(self):
return self.message
不,不禁止“消息”。它刚刚被弃用。您的应用程序可以使用message正常工作。当然,您可能希望消除弃用错误。
当您为应用程序创建自定义的Exception类时,其中许多类不仅仅是从Exception派生的,而是从其他类派生的,比如ValueError或类似的类。然后你必须适应他们对变量的使用。
如果应用程序中有许多异常,通常最好为所有异常设置一个通用的自定义基类,这样模块的用户就可以
try:
...
except NelsonsExceptions:
...
在这种情况下,您可以在那里执行所需的__init__和__str__,因此不必对每个异常重复执行。但是,简单地调用消息变量而不是消息就行了。
在任何情况下,您只需要__init__或__str__,如果您执行了与Exception本身不同的操作。因为如果你不赞成,你就需要两者,否则你会得到一个错误。这不是每个类需要的大量额外代码。