混合应该使用父属性吗?

24

我正在开发一个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)
2个回答

22

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

  • Mixin类不应该从任何基类(除了object)派生
  • 但是,它们可能假定它们将与混合在一起的类会从某个基类派生,因此它们可能假定基类存在或假定该类具有某些属性。

这两个规则使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(MixinBPlusOne,MixinBMinusOne, Base):
    """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 REST framework 的示例也非常优秀:Django REST framework mixins


8
混入类必须按相反的顺序放置:class Mixer(MixinBPlusOne, MixinBMinusOne, Base):。在这种情况下,混入类的顺序不重要,但基类必须在列表中排在最后。 - Vladimir Prudnikov
修正了顺序为class Mixer(<Mixins>, Base) - Niko Pasanen
你确定“Mixin类不应该从任何基类(除了object)派生”吗?那“Mixin不应该与其他父类有共同的祖先,除了object”怎么样? - Niko Pasanen

5

使用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

不错的回答 :) 只是请注意,abstractproperty 已被弃用,建议使用 abstractmethod: "@abc.abstractproperty Deprecated since version 3.3: It is now possible to use property, property.getter(), property.setter() and property.deleter() with abstractmethod(), making this decorator redundant." https://docs.python.org/3/library/abc.html - Laurent Lyaudet

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接