在现代Python中声明自定义异常的正确方法?

在现代Python中声明自定义异常的正确方法?,python,exception,custom-exceptions,Python,Exception,Custom Exceptions,在现代Python中,声明自定义异常类的正确方法是什么?我的主要目标是遵循其他异常类的任何标准,以便(例如)异常中包含的任何额外字符串都由捕获异常的任何工具打印出来 所谓“现代Python”,我指的是将在Python 2.5中运行的东西,但对于Python 2.6和Python 3来说是“正确的”。*做事方式。我所说的“自定义”是指一个异常对象,它可以包含关于错误原因的额外数据:一个字符串,也可能是与异常相关的其他任意对象 我被Python 2.6.2中的以下弃用警告绊倒了: >>&

在现代Python中,声明自定义异常类的正确方法是什么?我的主要目标是遵循其他异常类的任何标准,以便(例如)异常中包含的任何额外字符串都由捕获异常的任何工具打印出来

所谓“现代Python”,我指的是将在Python 2.5中运行的东西,但对于Python 2.6和Python 3来说是“正确的”。*做事方式。我所说的“自定义”是指一个异常对象,它可以包含关于错误原因的额外数据:一个字符串,也可能是与异常相关的其他任意对象

我被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
的属性有特殊意义,这似乎很疯狂。据我所知,在2.5版本中,这个属性确实有一个特殊的含义,他们正试图将其弃用掉,所以我猜这个名称(仅此一个)现在是被禁止的?啊

我还模糊地意识到
Exception
有一些神奇的参数
args
,但我从来不知道如何使用它。我也不确定这是一种正确的前进方式;我在网上找到的很多讨论都表明他们试图在Python3中去掉args


更新:有两个答案建议覆盖
\uuuuuuu init\uuuuuuu
\uuuuuu str\uuuuu
/
\uuuuuuu unicode\uuuuu
。这似乎需要大量的键入,是不是有必要?

您应该重写
\uuu repr\uuu
\uuuu unicode\uuu
方法,而不是使用message,您在构造异常时提供的参数将位于异常对象的
args
属性中。

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

类MyException(异常):
通过
要覆盖某些内容(或传递额外参数),请执行以下操作:

class MyException(Exception):
    pass

raise MyException("My hovercraft is full of eels")
类验证错误(异常):
定义初始化(自身、消息、错误):
#使用所需的参数调用基类构造函数
超级()。\uuuu初始化\uuuu(消息)
#现在为您的自定义代码。。。
self.errors=错误
这样,您就可以将错误消息的dict传递给第二个参数,然后使用
e.errors
访问它

在Python 2中,您必须使用稍微复杂一点的
super()

super(ValidationError,self)。\uuuuu初始化(消息)
否,“消息”不被禁止。它只是被弃用了。您的应用程序可以很好地使用消息。当然,您可能希望消除弃用错误

当您为应用程序创建自定义异常类时,其中许多类不仅仅是从Exception子类,而是从其他类(如ValueError或类似类)子类。然后你必须适应他们对变量的使用

如果您的应用程序中有许多异常,通常最好为所有异常都使用一个通用的自定义基类,这样模块的用户就可以这样做

try:
    ...
except NelsonsExceptions:
    ...
在这种情况下,您可以执行那里需要的
\uuuuu init\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu和uuuuuuuuuuuuuu。但是简单地调用message变量而不是message就可以了


在任何情况下,如果您执行与异常本身不同的操作,则只需要
\uuuuuu init\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu。因为如果不赞成,那么你就需要两者,否则你会得到一个错误。这并不是每个类需要多少额外代码

对于现代Python例外情况,您不需要滥用
.message
,也不需要重写
.str\uuuuuuuuuuuu()
.repr\uuuuuu()
或其任何内容。如果引发异常时只需要一条信息性消息,请执行以下操作:

class MyException(Exception):
    pass

raise MyException("My hovercraft is full of eels")
这将提供一个以MyException结尾的回溯:我的气垫船上满是鳗鱼

如果希望从异常中获得更大的灵活性,可以传递字典作为参数:

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
子类化


查看在使用一个或多个属性时默认情况下异常的工作方式(省略回溯):

因此,您可能希望有一种“异常模板”,以兼容的方式作为异常本身工作:

>>> nastyerr = NastyError('bad thing happened')
>>> raise nastyerr
NastyError: bad thing happened

>>> raise nastyerr()
NastyError: bad thing happened

>>> raise nastyerr('code is broken')
NastyError: ('bad thing happened', 'code is broken')
使用这个子类可以很容易地做到这一点

class ExceptionTemplate(Exception):
    def __call__(self, *args):
        return self.__class__(*(self.args + args))
# ...
class NastyError(ExceptionTemplate): pass
如果您不喜欢默认的类似元组的表示,只需将
\uuuu str\uuuu
方法添加到
ExceptionTemplate
类中,如:

    # ...
    def __str__(self):
        return ': '.join(self.args)
你会有

>>> raise nastyerr('code is broken')
NastyError: bad thing happened: code is broken
“在现代Python中声明自定义异常的正确方法?” 这很好,除非您的异常实际上是一种更具体的异常:

class MyException(Exception):
    pass
或者更好(可能是完美的),而不是
pass
给出一个docstring:

class MyException(Exception):
    """Raise for my specific kind of exception"""
子类化异常子类 从

例外情况

所有内置的、非系统退出的异常都派生自此类。 所有用户定义的异常也应由此派生 班级

这意味着如果您的异常是一种更具体的异常类型,则将该异常划分为子类,而不是通用的
异常
(结果将是您仍然按照文档建议从
异常派生)。此外,您至少可以提供一个docstring(并且不必强制使用
pass
关键字):

设置您使用自定义
\uuuuu init\uuuuu
创建的属性。避免将dict作为位置参数传递
class MyException(Exception):
    """Raise for my specific kind of exception"""
class MyAppValueError(ValueError):
    '''Raise when my specific value is wrong'''
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) 
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
>>> 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'
class InvalidInputError(Exception):
    def __init__(self, msg):
        self.msg = msg
    def __str__(self):
        return repr(self.msg)

inp = int(input("Enter a number between 1 to 10:"))
try:
    if type(inp) != int or inp not in list(range(1,11)):
        raise InvalidInputError
except InvalidInputError:
    print("Invalid input entered")
class CustomExceptionName(Exception):
    """Exception raised when very uncommon things happen"""
    pass
class CustomExceptionName(Exception):
    """Still an exception raised when uncommon things happen"""
    def __init__(self, message, payload=None):
        self.message = message
        self.payload = payload # you could add more args
    def __str__(self):
        return str(self.message) # __str__() obviously expects a string to be returned, so make sure not to send any other data types
try:
    raise CustomExceptionName("Very bad mistake.", "Forgot upgrading from Python 1")
except CustomExceptionName as error:
    print(str(error)) # Very bad mistake
    print("Detail: {}".format(error.payload)) # Detail: Forgot upgrading from Python 1
class MyProjectError(Exception):
    """A base class for MyProject exceptions."""
class CustomError(MyProjectError):
    def __init__(self, *args, **kwargs):
        super().__init__(*args)
        self.foo = kwargs.get('foo')
 raise CustomError('Something bad happened', foo='foo')
class CustomError(Exception):
    pass

raise CustomError("Hmm, seems like this was custom coded...")
class CustomError(Exception):
    __module__ = Exception.__module__

raise CustomError("Improved CustomError!")