2017-04-18 115 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從而打破檢查。