在现代Python中声明自定义异常类的正确方法是什么?我的主要目标是遵循其他异常类所具有的任何标准,以便(例如)由捕获异常的任何工具打印出异常中包含的任何额外字符串。
所谓"现代Python",我指的是在Python 2.5中运行但在Python 2.6和Python 3中"正确"的东西。做事的方法。我所说的"自定义"是指一个异常对象,它可以包含关于错误原因的额外数据:一个字符串,也可能是一些与异常相关的其他任意对象。
我在Python 2.6.2中遇到了下面的弃用警告:
1 2 3 4 5 6 | >>> 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 |
我也模糊地意识到
更新:有两个答案建议重写
也许我错过了这个问题,但为什么不呢:
1 2 | class MyException(Exception): pass |
编辑:要覆盖某些东西(或传递额外的参数),请这样做:
1 2 3 4 5 6 7 8 | 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 |
这样,您就可以将dict的错误消息传递给第二个参数,然后使用
Python 3更新:在python3 +中,您可以使用稍微紧凑一点的
1 2 3 4 5 6 7 8 | 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异常,您不需要滥用
1 2 3 4 | class MyException(Exception): pass raise MyException("My hovercraft is full of eels") |
这将给出一个以
如果你想从异常中获得更多的灵活性,你可以传递一个字典作为参数:
1 2 3 4 5 | raise MyException({"message":"My hovercraft is full of animals","animal":"eels <div class="suo-content">[collapse title=""]<ul><li>"但这将在未来被弃用"——这仍然是为了弃用吗?Python 3.7似乎仍然乐于接受<wyn>Exception(foo, bar, qux)</wyn>。</li><li>自从上次尝试由于转换的痛苦而失败后,它还没有看到任何最近的工作来破坏它,但是这种用法仍然不受欢迎。我将更新我的答案来反映这一点。</li><li>@frnknstn,为什么不鼓励这样做?对我来说,这是个不错的习语。</li><li>首先,使用元组存储异常信息与使用字典存储异常信息相比没有什么好处。如果您对异常更改背后的原因感兴趣,请查看PEP352</li></ul>[/collapse]</div><p><center>[wp_ad_camp_1]</center></p><hr><blockquote> <hh1>"Proper way to declare custom exceptions in modern Python?"</hh1> </blockquote><p>这很好,除非你的异常是一种更特殊的异常:</P>[cc lang="python"]class MyException(Exception): pass |
或者更好(也许是完美的),而不是
1 2 | class MyException(Exception): """Raise for my specific kind of exception""" |
<
子类化异常子类/ hh2 >
从文档
Exception All built-in, non-system-exiting exceptions are derived from this class.
All user-defined exceptions should also be derived from this
class.
这意味着,如果您的异常是一种更具体的异常类型,则子类化该异常而不是通用的
1 2 | class MyAppValueError(ValueError): '''Raise when my specific value is wrong''' |
设置使用自定义
1 2 3 4 5 6 7 8 9 | 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) |
真的没有必要编写自己的
Maybe I missed the question, but why not:
1 2 | class MyException(Exception): pass |
同样,上面的问题是,为了捕获它,您要么必须指定它的名称(如果在其他地方创建,则需要导入它),要么捕获异常(但是您可能不准备处理所有类型的异常,您应该只捕获准备处理的异常)。类似的批评如下,但除此之外,这不是通过
Edit: to override something (or pass extra args), do this:
1 2 3 4 5 6 7 8 | 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 |
That way you could pass dict of error messages to the second param, and get to it later with e.errors
它还需要传入两个参数(除了
直接地说,它违反了Liskov可替换性。
我将演示这两个错误:
1 2 3 4 5 6 7 8 9 10 | >>> 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' |
相比:
1 2 | >>> MyAppValueError('foo', 'FOO', 'bar').message 'foo' |
see how exceptions work by default if one vs more attributes are used (tracebacks omitted):
1 2 3 4 5 | >>> raise Exception('bad thing happened') Exception: bad thing happened >>> raise Exception('bad thing happened', 'code is broken') Exception: ('bad thing happened', 'code is broken') |
所以你可能想要一种"异常模板",作为一个异常本身,以兼容的方式工作:
1 2 3 4 5 6 7 8 9 | >>> 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') |
使用这个子类可以很容易地做到这一点
1 2 3 4 5 | class ExceptionTemplate(Exception): def __call__(self, *args): return self.__class__(*(self.args + args)) # ... class NastyError(ExceptionTemplate): pass |
如果您不喜欢默认的类元表示,只需将
1 2 3 | # ... def __str__(self): return ': '.join(self.args) |
和你会有
1 2 | >>> raise nastyerr('code is broken') NastyError: bad thing happened: code is broken |
您应该覆盖
不,"消息"不是禁止的。它只是弃用。您的应用程序可以很好地使用消息。当然,您可能希望消除弃用错误。
当您为您的应用程序创建自定义异常类时,它们中的许多并不仅仅来自Exception,而是来自其他异常,如ValueError或类似的异常。然后你必须适应他们对变量的使用。
如果您的应用程序中有很多异常,通常最好为所有异常都提供一个通用的自定义基类,这样模块的用户就可以这样做
1 2 3 4 | try: ... except NelsonsExceptions: ... |
在这种情况下,您可以执行所需的
在任何情况下,您只需要在执行与Exception本身不同的操作时使用
从Python 3.8 (2018, https://docs.python.org/dev/whatsnew/3.8.html)开始,推荐的方法仍然是:
1 2 3 | class CustomExceptionName(Exception): """Exception raised when very uncommon things happen""" pass |
请不要忘记记录,为什么需要自定义异常!
如果你需要,这是处理异常的方法与更多的数据:
1 2 3 4 5 6 7 | 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 |
然后像这样取:
1 2 3 4 5 | try: raise CustomExceptionName("Very bad mistake.","Forgot upgrading from Python 1") except CustomExceptionName as error: print(str(error)) # Very bad mistake print("Detail: {}".format(self.payload)) # Detail: Forgot upgrading from Python 1 |
如果需要将大量数据传输到某个外部结构,您可能应该研究如何使用pythons
试试这个例子
1 2 3 4 5 6 7 8 9 10 11 12 | 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") |