在python

时间:2017-04-18 17:37:47

标签: python python-2.7 inheritance metaclass

我认为这是一个相当简单的问题,但未能找到令人满意的答案。简而言之,我想在父类的子类上强制执行合同而不向每个子类添加逻辑。代码示例如下:

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

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

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

1 个答案:

答案 0 :(得分:2)

首先:您错误地使用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摘要。

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

使用两个" 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元类。

现在创建一个实现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,从而破坏了检查。