明确定义的自定义异常通常比内置异常提供更多信息;例如AgeError,超过ValueError。所以一般来说,我会尽可能使用前者。但因此,我的代码中散布着大量raise foo from bar样板文件,只是为了传播自定义异常。这是我的意思的一个例子。如果不使用自定义异常,我只需编写:
class Person:
def set_age(self, age_as_string):
self.age = int(age_as_string)
Run Code Online (Sandbox Code Playgroud)
这可能会引发TypeError或ValueError,但由于调用者会处理它,所以单行就可以了。
但要使用自定义异常,我需要样板:
class AgeError(Exception):
pass
class Person:
def set_age(self, age_as_string):
try:
self.age = int(age_as_string)
except (TypeError, ValueError) as e:
raise AgeError from e
Run Code Online (Sandbox Code Playgroud)
从调用者的角度来看,这提供了更多信息,但代码成本增加了 300%(仅计算方法体),并且模糊了set_age.
有没有办法两全其美?我尝试在谷歌上搜索解决方案,但即使这个问题似乎也没有得到太多讨论。我最终找到的解决方案是使用异常传播装饰器,由于其出色的功能,编写起来很简单contextlib(如果您需要手动实现它,则稍微不那么简单):
from contextlib import contextmanager
@contextmananer
def raise_from(to_catch, to_raise):
try:
yield
except to_catch as e:
raise to_raise from e
Run Code Online (Sandbox Code Playgroud)
现在我只需要额外一行,这不会掩盖业务逻辑,甚至使错误处理逻辑更加明显(而且看起来很聪明):
class Person:
@raise_from(to_catch=(TypeError, ValueError), to_raise=AgeError)
def set_age(self, age_as_string):
self.age …Run Code Online (Sandbox Code Playgroud)