如何在Python中使用类装饰器混合行为?

时间:2016-07-15 22:04:57

标签: python-3.x inheritance decorator mixins python-decorators

我有一个有很多直接子类的基类。多个子类共享多个独立功能。这是Python的合作继承的一个很好的用例。但是,这些功能应该从外部包装行为,因此它们需要在方法解析顺序中更早。

func rotate(duration: CFTimeInterval = 1.0, degrees:Double, completionDelegate: AnyObject? = nil) {
    let rotateAnimation = CABasicAnimation(keyPath: "transform.rotation")
    rotateAnimation.fromValue = 0.0
    let radians = CGFloat(degrees * M_PI / degrees)
    rotateAnimation.toValue = CGFloat(radians)
    rotateAnimation.duration = duration

    if let delegate: AnyObject = completionDelegate {
        rotateAnimation.delegate = delegate
    }
    self.layer.addAnimation(rotateAnimation, forKey: nil)

    // Actually set the layer's property
    self.layer.transform = CATransform3DMakeRotation(radians, 0.0, 0.0, 1.0)
}

最好装饰子类。

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

在Python 3中这怎么可能?

1 个答案:

答案 0 :(得分: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

这与您想要的不完全相同,因为它在FeatureOnemethod类版本之间调用Base类的Child实现,而不是在Child做之前。您可以改为添加一个新的Grandchild类,它继承自您首先关注的Feature,而Child最后一个,如果您无法调整方法以按此顺序工作( Grandchild班的身体可能是空的。)

如果你真的想使用装饰器来翻转顺序,我认为你可以使它工作,装饰器为你构建一个“孙子”类(尽管它对正常的继承层次结构一无所知) 。这是对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

mixin类应该从他们自己的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类(例如@mixin(FeatureA, FeatureB, FeatureC, foo=42, bar=13, foobar=546))或几个嵌套的装饰器调用一起工作。最终班级的MRO将采用相同的方式。