在现代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_。这似乎需要很多打字,是吗?


当前回答

对我来说,这只是__init__和变量,但有时会进行测试。

我的示例:

Error_codes = { 100: "Not enough parameters", 101: "Number of special characters more than limits", 102: "At least 18 alphanumeric characters and list of special chars !@#$&*" }

class localbreak( Exception ) :
    Message = ""
    
    def __init__(self, Message):
        self.Message = Message
        return
    def __str__(self):
        print(self.Message)
        return "False"

### When calling ...
raise localbreak(Error_codes[102])

输出:

Traceback (most recent call last):   File "ASCII.py", line 150, in <module>
    main(OldPassword, Newpassword)   File "ASCII.py", line 39, in main
    result = read_input("1", "2", Newpassword, "4")                                     
    File "ASCII.py", line 69, in read_input
    raise localbreak(Error_codes[102]) At least 18 alphanumeric characters and list of special chars !@#$&*
__main__.localbreak: False

其他回答

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

class MyException(Exception):
    pass

要覆盖某些内容(或传递额外的参数),请执行以下操作:

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

这样,您可以将错误消息的dict传递给第二个参数,然后使用e.errors进行处理。

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

super(ValidationError, self).__init__(message)

从Python 3.9.5开始,我对上述方法有问题。然而,我发现这对我很有用:

class MyException(Exception):
    """Port Exception"""

然后它可以在代码中使用,例如:

try:
    raise MyException('Message')

except MyException as err:
    print (err)

不,不禁止“消息”。它刚刚被弃用。您的应用程序可以使用message正常工作。当然,您可能希望消除弃用错误。

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

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

try:
    ...
except NelsonsExceptions:
    ...

在这种情况下,您可以在那里执行所需的__init__和__str__,因此不必对每个异常重复执行。但是,简单地调用消息变量而不是消息就行了。

在任何情况下,您只需要__init__或__str__,如果您执行了与Exception本身不同的操作。因为如果你不赞成,你就需要两者,否则你会得到一个错误。这不是每个类需要的大量额外代码。

一个非常简单的方法:

class CustomError(Exception):
    pass

raise CustomError("Hmm, seems like this was custom coded...")

或者,在不打印__main__的情况下引发错误(可能看起来更干净整洁):

class CustomError(Exception):
    __module__ = Exception.__module__

raise CustomError("Improved CustomError!")

要正确定义自己的异常,您应该遵循以下几个最佳实践:

定义从Exception继承的基类。这将允许轻松捕捉与项目相关的任何异常:类MyProjectError(异常):“”“MyProject异常的基类。”“”在单独的模块中组织异常类(例如exceptions.py)通常是一个好主意。要创建特定异常,请将基本异常类子类化。类CustomError(MyProjectError):“”“MyProject的自定义异常类。”“”您还可以子类化自定义异常类以创建层次结构。若要向自定义异常添加对额外参数的支持,请使用可变数量的参数定义__init__()方法。调用基类的__init__(),向其传递任何位置参数(请记住BaseException/Exception需要任意数量的位置参数)。为实例存储额外参数,例如:类CustomError(MyProjectError):定义__init__(self,*args,**kwargs):super()__init__(*参数)self.fo=kwargs.get('fo')要使用额外的参数引发此类异常,可以使用:raise CustomError(“发生了一些错误”,foo='fo')

这种设计遵循Liskov替换原则,因为您可以用派生异常类的实例替换基本异常类的一个实例。此外,它还允许您使用与父类相同的参数创建派生类的实例。