python自定义异常




在现代Python中声明自定义异常的正确方法是什么? (4)

“在现代Python中声明自定义异常的正确方法是什么?”

这很好,除非你的异常真的是一种更具体的异常:

class MyException(Exception):
    pass

或者更好(也许完美),而不是pass给文档字符串:

class MyException(Exception):
    """Raise for my specific kind of exception"""

子类异常子类

来自docs

Exception

所有内置的,非系统退出的异常都源自这个类。 所有用户定义的异常也应该从这个类派生。

这意味着如果您的异常是一种更具体的异常,则将该异常的子类替换为通用的Exception (并且结果将是您仍然从文档推荐的Exception派生出来的Exception )。 此外,你至少可以提供一个文档字符串(而不是强制使用pass关键字):

class MyAppValueError(ValueError):
    '''Raise when my specific value is wrong'''

使用自定义__init__设置您自己创建的属性。 避免将字典作为位置参数传递,未来的代码用户会感谢您。 如果您使用弃用的消息属性,则自行分配它将避免弃用警告:

class MyAppValueError(ValueError):
    '''Raise when a specific subset of values in context of app is wrong'''
    def __init__(self, message, foo, *args):
        self.message = message # without this you may get DeprecationWarning
        # Special attribute you desire with your Error, 
        # perhaps the value that caused the error?:
        self.foo = foo         
        # allow users initialize misc. arguments as any other builtin Error
        super(MyAppValueError, self).__init__(message, foo, *args) 

真的不需要编写自己的__str____repr__ 。 内置的非常好,你的合作继承可以确保你使用它。

对最佳答案的评论

也许我错过了这个问题,但为什么不:

class MyException(Exception):
    pass

同样,上面的问题是,为了捕获它,你要么专门命名它(如果在其他地方创建它,则要导入它)或捕获异常(但是你可能不准备处理所有类型的异常,你应该只捕捉你准备处理的异常)。 类似的批评,以下,但另外,这不是通过super初始化的方式,如果您访问消息属性,您会得到一个DeprecationWarning

编辑:重写某些内容(或传递额外的参数),请执行以下操作:

class ValidationError(Exception):
    def __init__(self, message, errors):

        # Call the base class constructor with the parameters it needs
        super(ValidationError, self).__init__(message)

        # Now for your custom code...
        self.errors = errors

这样你就可以将错误消息的字典传递给第二个参数,并在晚些时候使用e.errors进行处理

它还需要准确地传递两个参数(除了self 。不多也不少。 这是一个有趣的限制,未来的用户可能不会感激。

直接 - 它违反了Liskov可替代性

我将演示这两个错误:

>>> ValidationError('foo', 'bar', 'baz').message

Traceback (most recent call last):
  File "<pyshell#10>", line 1, in <module>
    ValidationError('foo', 'bar', 'baz').message
TypeError: __init__() takes exactly 3 arguments (4 given)

>>> ValidationError('foo', 'bar').message
__main__:1: DeprecationWarning: BaseException.message has been deprecated as of Python 2.6
'foo'

相比:

>>> MyAppValueError('foo', 'FOO', 'bar').message
'foo'

在现代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那里收集到PEP-352那个属性在2.5中有一个特殊的含义,他们试图贬低它,所以我猜这个名字(现在只有一个)现在被禁止了? 啊。

我也模糊地意识到, Exception有一些神奇的参数args ,但我从来不知道如何使用它。 我也不确定这是做事的正确方法; 我在网上发现的很多讨论都表明他们试图取消Python 3中的参数。

更新:两个答案建议覆盖__init____str__ / __unicode__ / __repr__ 。 这似乎很多打字,是否有必要?


不,“消息”不被禁止。 它只是被弃用。 您的应用程序将使用消息正常工作。 但是,当然,您可能希望摆脱弃用错误。

当你为你的应用程序创建自定义的Exception类时,它们中的很多不仅仅是来自Exception的子类,而是其他类,比如ValueError或类似的。 然后你必须适应他们对变量的使用。

如果您的应用程序中有很多例外情况,那么为所有这些应用程序提供一个通用的自定义基类通常是个好主意,以便您的模块的用户可以执行

try:
    ...
except NelsonsExceptions:
    ...

在这种情况下,您可以在那里__init__ and __str__所需的__init__ and __str__ ,因此您不必为每个异常都重复一遍。 但是简单地调用消息变量除了消息之外还有其他的窍门。

在任何情况下,如果您执行与Exception本身不同的操作,您只需要__init__ or __str__ 。 而且,因为如果弃用,那么您需要这两者,否则您会得到一个错误。 这不是每个班级所需的额外代码。 ;)


使用现代Python异常,您不需要滥用.message ,或覆盖.__str__().__repr__()或其中任何一个。 如果所有你想要的是一个提供异常信息的消息,请执行以下操作:

class MyException(Exception):
    pass

raise MyException("My hovercraft is full of eels")

这将以MyException: My hovercraft is full of eels结束回溯MyException: My hovercraft is full of eels

如果你想要更多的灵活性,你可以传递一个字典作为参数:

raise MyException({"message":"My hovercraft is full of animals", "animal":"eels"})

然而,要想在这个区域中看到这些细节则要复杂一些。 它们存储在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


您应该重写__repr____unicode__方法而不是使用消息,您在构造异常时提供的args将位于异常对象的args属性中。





exception