2017-04-18 82 views
0

我有我认为是一个非常简单的问题,但一直没能找到满意的答案。简而言之,我想在父级的子类上执行合约,而不向每个子类添加逻辑。下面的代码示例:装饰python儿童班的功能

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解决方案,我很乐意改变我的代码库。

+1

你看过'__new __()'方法吗? –

+0

@ IgnacioVazquez-Abrams不是一个不合理的解决方案。我希望在方法层面有更直观的一些,但如果我找不到更好的东西,可能会默认。 –

回答

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

只实现特定的检查,但仍然是抽象你检查类:

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从而打破检查。