异常传播decorator是一个好模式吗?

2024-10-03 04:32:05 发布

您现在位置:Python中文网/ 问答频道 /正文

定义良好的自定义异常通常比内置异常更具信息性;例如AgeErrorValueError更具信息性。所以总的来说,我尽量用前者。但结果是,我的代码中到处都是raise foo from bar样板文件,只是为了传播一个自定义异常。这是我的意思的一个例子。如果不使用自定义异常,我只想写:

class Person:
    def set_age(self, age_as_string):
        self.age = int(age_as_string)

这可能会引发TypeErrorValueError,但由于调用者将处理它,所以一行就可以了。你知道吗

但要使用自定义例外,我需要样板文件:

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

从调用者的角度来看,这会提供更多的信息,但代码成本会增加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

现在我只需要一个额外的行,它不会模糊业务逻辑,甚至会使错误处理逻辑更加明显(而且看起来很聪明):

class Person:
    @raise_from(to_catch=(TypeError, ValueError), to_raise=AgeError)
    def set_age(self, age_as_string):
        self.age = int(age_as_string)

所以我对这个解决方案很满意。但是,由于像这样简单的解决方案不太可能仍然存在任何未解决的问题,我担心我可能遗漏了一些东西。使用raise_from修饰符是否有我没有考虑过的缺点?或者,减少raise foo from bar样板文件的必要性是否表明我做错了什么?你知道吗


Tags: 文件tofromselfagestringdefas