mixin应该使用父属性吗?

时间:2016-04-18 09:50:29

标签: python design-patterns mixins

我正在开发一个python项目,我需要使用大约20个不同的类来实现一系列功能,例如:“download”,“parse”,“update”等。

使用超类可以很容易地分解几个功能,因为所有代码对于所有代码都是相同的。

但有时候,特别是对于“解析”方法,我有10个必须实现相同算法的类和10个需要特定算法的类。

基于我对python的了解,使用mixins可以很容易地分解这种行为。

但即使“解析”算法相同,这里也存在问题,我需要将标记应用于解析的条目,并且此标记特定于每个类。我想知道这是否是一种使用class属性的正确方法,该属性仅由mixin用于实现此目标。

这段代码给出了如何使用属性的示例:

class MyMixin():
    def parse(self):
        print(self.tag)
        ...

class MyClass(MyMixin):
    tag = 'mytag'

我已经在一些框架(http://www.django-rest-framework.org/api-guide/generic-views/)中看到了它,但我很想知道社区的意见是什么。

==========================

修改

总结一个具体的例子,我应该这样写:

class MyMixin():
    def do_something(self):
        print(self.tag)

class MyClass(MyMixin):
    tag = 'mytag'

if __name__ == '__main__':
    c = MyClass()
    c.do_something()

或那:

class MyMixin():
    def do_something(self, tag):
        print(tag)

class MyClass(MyMixin):
    tag = 'mytag'

if __name__ == '__main__':
    c = MyClass()
    c.do_something(c.tag)

2 个答案:

答案 0 :(得分:1)

abc模块的帮助下,您可以获得额外的安全性:

from abc import abstractproperty, ABCMeta

class Parser(object):
    __metaclass__ = ABCMeta

    @abstractproperty
    def tag(self):
        pass

class Concrete(Parser):

    @property
    def tag(self):
        return 'concrete'

print Concrete().tag  # => prints 'concrete'

class Incomplete(Parser):
    pass

# Raises error:
# TypeError: Can't instantiate abstract class Incomplete with abstract methods tag
Incomplete()

(Python 3的代码可能略有不同)

这样就可以很好地及早地捕获错误,而不是在访问属性时捕获错误。

此外,PyCharm警告该类在定义中不完整。其他静态分析工具也可能会选择它。

enter image description here

答案 1 :(得分:1)

您可以使用mixins在其他类中实现合成。这样,您可以将功能委托给mixin并在其他类中重用mixin。因此,使用python时,您对混合模块有两个规则:

  • Mixin类不应派生自任何基类(对象除外)
  • 但是他们可能会假设他们将要混合的课程 从某个基类派生的,因此他们可以假设该基类是 他们认为该类具有某些属性。

这两个规则将继承设置为继承。

因此,要回答您的问题,是的,如果您确定父级具有这些属性,则可以使用父级属性。

一个小例子(来自:https://mail.python.org/pipermail/tutor/2008-May/062005.html):

class Base(object):
    """Base class for mixer classes. All mixin classes
    require the classes they are mixed in with to be
    instances of this class (or a subclass)."""

    def __init__(self,b):
        self.b = b # Mixin classes assume this attribute will be present

class MixinBPlusOne(object):
    """A mixin class that implements the print_b_plus_one
    method."""

    def __init__(self):
        print 'MixinBPlusOne initialising'

    def print_b_plus_one(self):
        print self.b+1

class MixinBMinusOne(object):
    """A mixin class that implements the print_b_minus_one
    method."""

    def __init__(self):
        print 'MixinBMinusOne initialising'

    def print_b_minus_one(self):
        print self.b-1

class Mixer(Base,MixinBPlusOne,MixinBMinusOne):
    """A mixer class (class that inherits some mixins), this
    will work because it also inherits Base, which the
    mixins expect."""

    def __init__(self,b):
        # I feel like I should be using super here because 
        # I'm using new-style classes and multiple
        # inheritance, but the argument list of
        # Base.__init__ differs from those of the Mixin
        # classes, and this seems to work anyway.
        Base.__init__(self,b)
        MixinBPlusOne.__init__(self)
        MixinBMinusOne.__init__(self)

class BrokenMixer(MixinBPlusOne,MixinBMinusOne):
    """This will not work because it does not inherit Base,
    which the mixins expect it to do."""

    pass

m = Mixer(9)
m.print_b_plus_one()
m.print_b_minus_one()

m = BrokenMixer(9)
m.print_b_plus_one() # It'll crash here.

django restframework示例也非常好:django rest framework mixins