2016-07-15 33 views
1

我有一個基類,有很多直接子類。有多個獨立的功能由多個子類共享。這對於Python的合作繼承來說是一個很好的用例。但是,這些功能應該從外部包裝行爲,所以它們需要在方法解析順序中更早。如何在Python中使用類裝飾器混合行爲?

class WrappedSub(FeatureA, FeatureB, FeatureC, RealSub): 

    def __init__(self, *args, **kwargs): 
     FeatureA.__init__(foo=42) 
     FeatureB.__init__(bar=13) 
     FeatureC.__init__(foobar=546) 
     RealSub.__init__(*args, **kwargs) 

class RealSub(Base): 
    # Lots of code ... 

這將是很好的修飾兒童班。

@Mixin(FeatureA, 42) 
@Mixin(FeatureB, 13) 
@Mixin(FeatureC, 546) 
class RealSub(Base): 
    # Lots of code ... 

準確地說,我需要一個@Mixin裝飾,其中下面的第一個塊是等同於第二。

@Mixin(Sub, *feature_args, **feature_kwargs) 
class RealSub: 
    # Lots of code ... 

class RealSub: 
    # Lots of code ... 
class WrappedSub(Feature, RealSub): 
    def __init__(self, *sub_args, **sub_kwargs): 
     Feature.__init__(self, *feature_args, **feature_kwargs) 
     RealSub.__init__(self, *sub_args, **sub_kwargs) 
RealSub = WrappedSub 

在Python 3中這怎麼可能?

回答

3

您可能可以使用Python的協作式多重繼承系統來編寫您的mixin類,而不是試圖將它們實現爲類裝飾器。這就是我通常對Python OOP中使用的術語「mixin」的理解。

class Base: 
    def method(self, param): 
     value = param + 18 
     return value 

class FeatureOne:    # this could inherit from Base 
    def method(self, param): 
     if param == 42: 
      return 13 
     else: 
      return super().method(param) # call next class in inheritance chain 

class Child(FeatureOne, Base): 
    def method(self, param): 
     value = super().method(param) 
     value *= 2 
     return value 

這是不太一樣的,你想要的東西,因爲它調用FeatureOne類的的BaseChild班版本之間method實現,而不是以前Child做它的事。如果無法調整方法以按此順序工作(Grandchild類的主體可能爲空),您可以添加一個新的Grandchild類,該類繼承自您首先關注的Feature和最後Child

如果你真的想使用裝飾器翻轉順序,我認爲你可以讓它工作,裝飾器爲你構建一個「孫子」類(雖然它不知道任何關於正常繼承層次結構)。下面是在mixin裝飾的作品幾乎就像你一個粗略的嘗試想:

def mixin(*mixin_classes, **mixin_kwargs): # decorator factory function 
    def decorator(cls): # decorator function 
     class wrapper(*mixin_classes, cls): 
      def __init__(self, *args, **kwargs): 
       wrapped_kwargs = mixin_kwargs.copy() # use the passed kwargs to update the 
       wrapped_kwargs.update(kwargs)  # mixin args, so caller can override 
       super().__init__(*args, **wrapped_kwargs) 
     # maybe modify wrapper's __name__, __qualname__, __doc__, etc. to match cls here? 
     return wrapper 
    return decorator 

的混入類應該調用super().__init__(*args, **kwargs)從自己__init__方法(如果有的話),但他們可以接受(而不是傳遞)關鍵字只有自己的論點,他們希望通過mixin裝飾進行傳遞:

class FeatureOne: 
    def __init__(self, *args, foo, **kwargs): # note that foo is a keyword-only argument 
     self.foo = foo 
     super().__init__(*args, **kwargs) 

    def method(self, param): 
     if param == self.foo: 
      return 13 
     else: 
      return super().__method__(param) 

@mixin(FeatureOne, foo=42) 
class Child(Base): 
    def method(self, param): 
     return super().method(param) * 2 

的裝飾應該工作,要麼所有的混入類傳遞給一個裝飾呼叫(例如@mixin(FeatureA, FeatureB, FeatureC, foo=42, bar=13, foobar=546)),或具有多個嵌套的裝飾調用。最後一堂課的MRO將是相同的。

+0

我想過合作社繼承和更新問題。正如你所說,問題是功能包裝子類的行爲。也許合作繼承可以和'@ Mixin'類裝飾器結合使用?另一個想法是複製包裝要素類對象並將包裝後的子類添加到它的__bases__中。你認爲這可行嗎? – danijar