child 类 在 python 中的修饰函数

Decorating functions of child classes in python

我认为这是一个非常简单的问题,但未能找到令人满意的答案。简而言之,我想在 parent 的 child class 上执行合同,而不向每个 child class 添加逻辑。下面的代码示例:

class A(object):

    @abc.abstractmethod
    def do_thing(self, input):
        raise NotImplementedError

class A1(A):

    def do_thing_decorator(self, do_thing_func):
        def checked_do_thing(input):
            check = do_thing_func(input)
            if check != 1:
                raise ValueError
            return check
        return checked_do_thing

所以问题是如何auto-decorateclasses继承A1实现的do_thing功能?假设还有 A2 class 以及略有不同的检查。

我的初步调查表明元classes 是可以采用的方法,但很难找到关于它们如何工作的很好的解释。理想情况下寻找在 python 2.x 中起作用的东西,但如果只有 3.x 解决方案,我很乐意更改我的代码库。

首先:您错误地使用了abc模块(see the docs)。你的 class A 应该有 abc.ABCMeta 作为元 class。因此,如果您已经在使用元 class,您可以扩展它以发挥您的优势。

一个元 class 继承自 abc.ABCMeta 使 abstractmethod 工作并修饰 do_thing:

from abc import ABCMeta, abstractmethod

class DecoratingMeta(ABCMeta):
    def __new__(cls, *args):
        new_class = super(DecoratingMeta, cls).__new__(cls, *args)
        # decorating do_thing manually
        new_class.do_thing = new_class.do_thing_decorator(new_class.do_thing)
        return new_class

现在你的抽象基础 class 带有一个什么也不做的默认检查装饰器:

# class Abstract(metaclass=ABCMeta): in python3
class Abstract(object):
    __metaclass__ = DecoratingMeta  # remove this line in python3
    @abstractmethod
    def do_thing(self, input):
        pass

    @classmethod
    def do_thing_decorator(cls, function):
        return function     # default decorator that does nothing

请注意,在这种情况下,do_thing_decorator 必须是 class 方法。 对于在 python3python2 中工作的元 classes,请参阅 six

您的检查器 class 仅实现特定检查器但仍然是抽象的:

class Checker(Abstract):
    @classmethod
    def do_thing_decorator(cls, function):
        def do_checked_thing(self, input):
            check = function(self, input)  # NOT self.do_thing(input) else recursion error
            if check != 1:
                raise ValueError("Check failed")
            return check
        return do_checked_thing

请注意,您编写的行 check = do_thing_func(input) 会导致递归错误。

以及您的具体 class 示例实现 do_thing:

class Concrete(Checker):
    def do_thing(self, input):
        return input    # sample implementation

您可以验证 do_thing(1) 成功并且 do_thing(2) 失败

c = Concrete()

c.do_thing(1)
try:
    c.do_thing(2)
except ValueError:
    print("check failed")

这种方法的缺点是您无法将 do_thing_decorator 抽象化。

所以这已经是很多文本了,但是如果您根本不想使用任何元 classes,那么有一个更简单的方法:

编写一个class,使用两个"abstract"方法在do_thing方法中执行检查:

class Abstract(object):
    def do_thing_func(self, input):
        raise NotImplementedError()

    def check_do_thing(self, result):
        raise NotImplementedError()

    # don't override this method
    def do_thing(self, input):
        result = self.do_thing_func(input)
        self.check_do_thing(result)  # may raise
        return result  # if it does not raise return the result

请注意 do_thing_funccheck_do_thing 并不是真正的抽象,您仍然可以实例化 Abstract 类型的对象。如果您需要它们是抽象的,请在此处使用标准 abc.ABCMeta 元 class。

现在创建一个实现 check_do_thing

的检查器 class
class Checker(Abstract):
    def check_do_thing(self, result):
        if result != 1:
            raise ValueError("check failed")

这就变得简单多了,因为我们这里不需要装饰器。

最后是实现 do_thing_func

的具体 class
class Concrete(Checker):
    def do_thing_func(self, input):
        return input    # sample implementation

注意 Concrete 现在必须实现 do_thing_func 但是当你使用 class 时你必须调用 do_thing.

这里的缺点是您仍然可以覆盖 do_thing 从而破坏检查。