异常传播的装饰器是一个好的模式吗?

时间:2016-02-06 04:28:12

标签: python python-decorators

明确定义的自定义异常通常比内置异常更具信息性;例如比AgeError更多ValueError。所以一般来说,我尽可能地使用前者。但是由于这个原因,我的代码中充斥着许多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样板,表明我做错了什么?

0 个答案:

没有答案