在现代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__ 。 这似乎很多打字,是否有必要?


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

class MyException(Exception):
    pass

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

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来获取它


Python 3更新:在Python 3+中,你可以使用这个稍微更紧凑的super()

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

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

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

使用现代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"})

但是,要在一个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

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

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

class MyException(Exception):
    pass

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

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

子类异常子类

来自文档

Exception

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

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

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

使用自定义__init__设置您自己创建的属性。 避免将字典作为位置参数传递,未来的代码用户会感谢您。 如果您使用的过时信息属性,分配给它自己会避免DeprecationWarning

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'
链接地址: http://www.djcxy.com/p/3541.html

上一篇: Proper way to declare custom exceptions in modern Python?

下一篇: Why should I use the keyword "final" on a method parameter in Java?