Nel*_*son 1176 python exception custom-exceptions
在现代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
Run Code Online (Sandbox Code Playgroud)
看起来很疯狂,BaseException
对于名为的属性具有特殊含义message
.我从PEP-352收集到该属性确实在2.5中有特殊含义他们试图弃用,所以我猜这个名字(而且仅此一个)现在被禁止了?啊.
我也模糊地意识到它Exception
有一些神奇的参数args
,但我从来不知道如何使用它.我也不确定这是向前发展的正确方法; 我在网上发现的很多讨论都表明他们试图在Python 3中废除args.
更新:两个答案建议覆盖__init__
,和__str__
/ __unicode__
/ __repr__
.这似乎很多打字,是否有必要?
gah*_*ooa 1208
也许我错过了这个问题,但为什么不呢:
class MyException(Exception):
pass
Run Code Online (Sandbox Code Playgroud)
编辑:覆盖某些内容(或传递额外的args),执行以下操作:
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
Run Code Online (Sandbox Code Playgroud)
这样你可以将错误消息的dict传递给第二个参数,稍后再使用 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
Run Code Online (Sandbox Code Playgroud)
frn*_*stn 458
随着现代Python的例外,你并不需要滥用.message
,或覆盖.__str__()
或.__repr__()
或任何它.如果你想要的只是提出异常的信息,请执行以下操作:
class MyException(Exception):
pass
raise MyException("My hovercraft is full of eels")
Run Code Online (Sandbox Code Playgroud)
这将以追溯结束MyException: My hovercraft is full of eels
.
如果您希望从异常中获得更大的灵活性,可以将字典作为参数传递:
raise MyException({"message":"My hovercraft is full of animals", "animal":"eels"})
Run Code Online (Sandbox Code Playgroud)
但是,要在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"])
Run Code Online (Sandbox Code Playgroud)
仍然可以将多个项目传递给异常并通过元组索引访问它们,但这是非常不鼓励的(甚至打算暂时弃用).如果您确实需要多条信息并且上述方法对您来说还不够,那么您应该Exception
按照教程中的描述进行子类化.
class MyError(Exception):
def __init__(self, message, animal):
self.message = message
self.animal = animal
def __str__(self):
return self.message
Run Code Online (Sandbox Code Playgroud)
Aar*_*all 186
"在现代Python中声明自定义异常的正确方法?"
这很好,除非您的异常实际上是一种更具体的异常:
class MyException(Exception):
pass
Run Code Online (Sandbox Code Playgroud)
或者更好(也许更完美),而不是pass
给文档字符串:
class MyException(Exception):
"""Raise for my specific kind of exception"""
Run Code Online (Sandbox Code Playgroud)
来自文档
Exception
所有内置的,非系统退出的异常都派生自此类.所有用户定义的异常也应该从该类派生.
这意味着如果您的异常是一种更具体的异常,则将该异常子类化为通用Exception
(并且结果将是您仍然从Exception
文档推荐的那样派生的).此外,您至少可以提供docstring(而不是强制使用该pass
关键字):
class MyAppValueError(ValueError):
'''Raise when my specific value is wrong'''
Run Code Online (Sandbox Code Playgroud)
使用自定义设置您自己创建的属性__init__
.避免传递dict作为位置参数,未来的代码用户会感谢你.如果您使用已弃用的邮件属性,则自行分配将避免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)
Run Code Online (Sandbox Code Playgroud)
真的没有必要写自己的__str__
或__repr__
.内置的非常好,你的合作继承确保你使用它.
也许我错过了这个问题,但为什么不呢:
class MyException(Exception):
pass
Run Code Online (Sandbox Code Playgroud)
同样,上面的问题是,为了捕获它,你要么必须专门命名(如果在其他地方创建就导入它)或捕获异常,(但你可能不准备处理所有类型的异常,你应该只捕捉你准备处理的异常).类似的批评下面,但另外这不是初始化通过的方式super
,DeprecationWarning
如果你访问消息属性,你会得到一个:
编辑:覆盖某些内容(或传递额外的args),执行以下操作:
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
Run Code Online (Sandbox Code Playgroud)
这样你就可以将错误消息的dict传递给第二个参数,并在以后通过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'
Run Code Online (Sandbox Code Playgroud)
相比:
>>> MyAppValueError('foo', 'FOO', 'bar').message
'foo'
Run Code Online (Sandbox Code Playgroud)
myk*_*hal 47
见异常缺省情况下是如何工作的,如果一个VS多个属性使用(回溯略):
>>> raise Exception('bad thing happened')
Exception: bad thing happened
>>> raise Exception('bad thing happened', 'code is broken')
Exception: ('bad thing happened', 'code is broken')
Run Code Online (Sandbox Code Playgroud)
所以你可能希望有一种" 异常模板 ",以兼容的方式作为异常本身:
>>> 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')
Run Code Online (Sandbox Code Playgroud)
这可以通过这个子类轻松完成
class ExceptionTemplate(Exception):
def __call__(self, *args):
return self.__class__(*(self.args + args))
# ...
class NastyError(ExceptionTemplate): pass
Run Code Online (Sandbox Code Playgroud)
如果您不喜欢这种默认的类似于元组的表示,只需将__str__
方法添加到ExceptionTemplate
类中,例如:
# ...
def __str__(self):
return ': '.join(self.args)
Run Code Online (Sandbox Code Playgroud)
你会的
>>> raise nastyerr('code is broken')
NastyError: bad thing happened: code is broken
Run Code Online (Sandbox Code Playgroud)
Eug*_*ash 24
要正确定义您自己的异常,您应该遵循一些最佳实践:
定义基类继承Exception
。这将允许轻松捕获与项目相关的任何异常:
class MyProjectError(Exception):
"""A base class for MyProject exceptions."""
Run Code Online (Sandbox Code Playgroud)
在单独的模块(例如exceptions.py
)中组织异常类通常是一个好主意。
要创建特定异常,请子类化基本异常类。
要向自定义异常添加对额外参数的支持,请定义一个__init__()
具有可变数量参数的自定义方法。调用基类的__init__()
,将任何位置参数传递给它(记住BaseException
/Exception
期望任意数量的位置参数):
class CustomError(MyProjectError):
def __init__(self, *args, **kwargs):
super().__init__(*args)
self.foo = kwargs.get('foo')
Run Code Online (Sandbox Code Playgroud)
要使用额外的参数引发此类异常,您可以使用:
raise CustomError('Something bad happened', foo='foo')
Run Code Online (Sandbox Code Playgroud)
此设计遵循Liskov 替换原则,因为您可以用派生异常类的实例替换基本异常类的实例。此外,它还允许您使用与父类相同的参数创建派生类的实例。
fam*_*man 19
从Python 3.8(2018,https://docs.python.org/dev/whatsnew/3.8.html)开始,推荐的方法仍然是:
class CustomExceptionName(Exception):
"""Exception raised when very uncommon things happen"""
pass
Run Code Online (Sandbox Code Playgroud)
请不要忘记记录,为什么需要自定义例外!
如果需要,这是获取更多数据的异常的方法:
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
Run Code Online (Sandbox Code Playgroud)
并像他们一样取出它们:
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
Run Code Online (Sandbox Code Playgroud)
payload=None
使它成为可腌制的重要.在转储它之前,你必须打电话error.__reduce__()
.加载将按预期工作.
return
如果需要将大量数据传输到某些外部结构,您可能应该使用pythons 语句调查寻找解决方案.对我来说,这似乎更清晰/更pythonic.Java中使用了高级异常,当使用框架并且必须捕获所有可能的错误时,这有时会令人讨厌.
Yar*_*nko 11
请参阅一篇非常好的文章“ Python 异常的权威指南”。基本原则是:
BaseException.__init__
只用一个参数调用。还有关于组织(在模块中)和包装异常的信息,我建议阅读指南。
Gal*_*ses 10
为了最大程度地自定义,为了定义自定义错误,您可能需要定义一个继承自Exception
类的中间类,如下所示:
class BaseCustomException(Exception):
def __init__(self, msg):
self.msg = msg
def __repr__(self):
return self.msg
class MyCustomError(BaseCustomException):
"""raise my custom error"""
Run Code Online (Sandbox Code Playgroud)
从 Python 3.9.5 开始,我对上述方法遇到了问题。但是,我发现这对我有用:
class MyException(Exception):
"""Port Exception"""
Run Code Online (Sandbox Code Playgroud)
然后它可以在如下代码中使用:
try:
raise MyException('Message')
except MyException as err:
print (err)
Run Code Online (Sandbox Code Playgroud)
不,"消息"不被禁止.它刚刚被弃用了.您的应用程序将使用消息正常工作.但是,当然,您可能希望摆脱弃用错误.
为应用程序创建自定义异常类时,其中许多不仅仅是从Exception继承,而是从其他类(如ValueError或类似的)继承.然后你必须适应他们对变量的使用.
如果你的应用程序中有很多例外,那么为所有这些例子设置一个通用的自定义基类通常是一个好主意,这样你的模块用户就可以做了
try:
...
except NelsonsExceptions:
...
Run Code Online (Sandbox Code Playgroud)
在这种情况下,您可以在__init__ and __str__
那里执行所需的操作,因此您不必为每个例外重复它.但是简单地调用消息变量而不是消息就可以了.
在任何情况下,只需要__init__ or __str__
执行与Exception本身不同的操作.因为如果弃用,则需要两者,否则会出错.这不是每个课程所需的额外代码.;)
归档时间: |
|
查看次数: |
603764 次 |
最近记录: |