2016-07-15 59 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