【问题标题】:Should mixins use parent attributes?mixins 应该使用父属性吗?
【发布时间】:2016-08-09 23:46:47
【问题描述】:

我正在开发一个 python 项目,我需要使用大约 20 个不同的类来实现一系列功能,例如:“下载”、“解析”、“更新”等。

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

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

根据我对 python 的了解,这种行为可以通过使用 mixins 轻松分解。

但是即使“解析”算法相同,这里也存在问题,我需要对解析的条目应用一个标签,并且这个标签是针对每个类的。我想知道这是否是使用仅由 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)

【问题讨论】:

    标签: python design-patterns mixins


    【解决方案1】:

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

    • 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

    【讨论】:

    • Mixins 必须以相反的顺序放置:class Mixer(MixinBPlusOne, MixinBMinusOne, Base):。在这种情况下,Mixins 的顺序无关紧要,但基类必须在列表的最后。
    【解决方案2】:

    借助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 警告该类在定义处不完整。其他静态分析工具可能也可以做到这一点。

    【讨论】:

      猜你喜欢
      • 2011-01-21
      • 1970-01-01
      • 2012-05-26
      • 2014-10-18
      • 1970-01-01
      • 1970-01-01
      • 2019-05-30
      • 2012-06-25
      • 2018-10-11
      相关资源
      最近更新 更多