python中子类的装饰函数

Posted

技术标签:

【中文标题】python中子类的装饰函数【英文标题】:Decorating functions of child classes in python 【发布时间】:2017-09-14 16:26:09 【问题描述】:

我有一个我认为非常简单的问题,但一直未能找到令人满意的答案。简而言之,我想在不向每个子类添加逻辑的情况下对父类的子类强制执行合同。下面的代码示例:

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

所以问题是如何自动装饰由继承自A1 的类实现的do_thing 函数?假设还有一个 A2 类以及稍有不同的检查。

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

【问题讨论】:

你看过__new__()方法吗? @IgnacioVazquez-Abrams 不是一个不合理的解决方案。我希望在方法级别上有一些更直观的东西,但如果我找不到更好的东西,我可能会默认使用它。 【参考方案1】:

首先:您错误地使用了abc 模块(see the docs)。您的课程 A 应该有 abc.ABCMeta 作为元类。因此,如果您已经在使用元类,则可以将其扩展为您的优势。

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 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 在这种情况下必须是类方法。 对于在python3python2 中工作的元类,请参阅six。

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

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) 会导致递归错误。

还有你的具体类,带有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 抽象化。

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

编写一个类,使用两个“抽象”方法在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 元类。

现在创建一个实现check_do_thing的检查器类

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

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

最后是实现do_thing_func的具体类

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

请注意,Concrete 现在必须实现 do_thing_func,但是当您使用该类时,您必须调用 do_thing

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

【讨论】:

以上是关于python中子类的装饰函数的主要内容,如果未能解决你的问题,请参考以下文章

Python 函数式编程装饰器以及一些相关概念简介

如何装饰子类中的继承方法?

装饰器生成器迭代器及python中内置函数的使用

Python装饰器的实现和万能装饰器

python 装饰器

python -- 装饰器