在Python编程中,Mark Lutz提到了术语mixin。我有C/ c++ / c#的背景,以前没有听说过这个术语。什么是mixin?

从这个例子的字里行间(我链接了这个例子,因为它很长),我假设这是一个使用多重继承来扩展类的情况,而不是适当的子类化。这样对吗?

为什么我要这样做,而不是把新功能放到一个子类?就此而言,为什么混合/多重继承方法会比使用组合更好呢?

mixin与多重继承的区别是什么?这只是语义上的问题吗?


当前回答

Mixin提供了一种在类中添加功能的方法,即您可以通过将模块包含在所需的类中来与模块中定义的方法进行交互。虽然ruby不支持多重继承,但提供了mixin作为实现这一目标的替代方案。

下面是一个示例,说明如何使用mixin实现多重继承。

module A    # you create a module
    def a1  # lets have a method 'a1' in it
    end
    def a2  # Another method 'a2'
    end
end

module B    # let's say we have another module
    def b1  # A method 'b1'
    end
    def b2  #another method b2
    end
end

class Sample    # we create a class 'Sample'
    include A   # including module 'A' in the class 'Sample' (mixin)
    include B   # including module B as well

    def S1      #class 'Sample' contains a method 's1'
    end
end

samp = Sample.new    # creating an instance object 'samp'

# we can access methods from module A and B in our class(power of mixin)

samp.a1     # accessing method 'a1' from module A
samp.a2     # accessing method 'a2' from module A
samp.b1     # accessing method 'b1' from module B
samp.b2     # accessing method 'a2' from module B
samp.s1     # accessing method 's1' inside the class Sample

其他回答

我认为这里有一些很好的解释,但我想提供另一个角度。

In Scala, you can do mixins as has been described here but what is very interesting is that the mixins are actually 'fused' together to create a new kind of class to inherit from. In essence, you do not inherit from multiple classes/mixins, but rather, generate a new kind of class with all the properties of the mixin to inherit from. This makes sense since Scala is based on the JVM where multiple-inheritance is not currently supported (as of Java 8). This mixin class type, by the way, is a special type called a Trait in Scala.

它在定义类的方式中有所暗示: NewClass用SecondMixin和ThirdMixin扩展了FirstMixin …

我不确定CPython解释器是否做同样的事情(mixin class-composition),但我不会感到惊讶。同样,作为c++背景,我不会将ABC或“接口”称为等同于mixin——它们是类似的概念,但在使用和实现上有所不同。

也许举几个例子会有所帮助。

如果您正在构建一个类,并且希望它像字典一样工作,那么您可以定义所有必要的__ __方法。但这有点麻烦。作为一种替代方法,您可以只定义一些,并从UserDict继承(除了任何其他继承)。DictMixin(移动到集合。py3k中的DictMixin)。这将自动定义字典api的所有其余部分。

第二个例子:GUI工具包wxPython允许您创建具有多列的列表控件(例如,在Windows资源管理器中显示的文件)。默认情况下,这些列表相当基本。您可以添加额外的功能,例如通过单击列标题按特定列对列表进行排序的功能,可以从ListCtrl继承并添加适当的mixins。

这个答案旨在通过以下例子解释mixin:

自包含:简短,不需要知道任何库来理解示例。 Python,而不是其他语言。 可以理解,这里有来自其他语言(如Ruby)的例子,因为这个术语在这些语言中更常见,但这是一个Python线程。

它还应审议有争议的问题:

对于mixin的特征来说,多重继承是必要的还是不必要的?

定义

我还没有看到一个“权威”来源的引用清楚地说明什么是Python中的mixin。

我已经看到了mixin的两种可能的定义(如果它们被认为不同于其他类似的概念,如抽象基类),人们并不完全同意哪一种是正确的。

不同语言之间的共识可能有所不同。

定义1:无多重继承

mixin是这样一个类,该类的一些方法使用了类中没有定义的方法。

因此,该类并不意味着要被实例化,而是作为基类使用。否则,实例将具有在不引发异常的情况下无法调用的方法。

一些源代码添加的约束是类不能包含数据,只能包含方法,但我不明白为什么这是必要的。然而在实践中,许多有用的mixin没有任何数据,没有数据的基类使用起来更简单。

一个经典的例子是从<=和==中实现所有比较运算符:

class ComparableMixin(object):
    """This class has methods which use `<=` and `==`,
    but this class does NOT implement those methods."""
    def __ne__(self, other):
        return not (self == other)
    def __lt__(self, other):
        return self <= other and (self != other)
    def __gt__(self, other):
        return not self <= other
    def __ge__(self, other):
        return self == other or self > other

class Integer(ComparableMixin):
    def __init__(self, i):
        self.i = i
    def __le__(self, other):
        return self.i <= other.i
    def __eq__(self, other):
        return self.i == other.i

assert Integer(0) <  Integer(1)
assert Integer(0) != Integer(1)
assert Integer(1) >  Integer(0)
assert Integer(1) >= Integer(1)

# It is possible to instantiate a mixin:
o = ComparableMixin()
# but one of its methods raise an exception:
#o != o 

这个特殊的例子可以通过functools. total_ordered()装饰器来实现,但这里的游戏是重新发明轮子:

import functools

@functools.total_ordering
class Integer(object):
    def __init__(self, i):
        self.i = i
    def __le__(self, other):
        return self.i <= other.i
    def __eq__(self, other):
        return self.i == other.i

assert Integer(0) < Integer(1)
assert Integer(0) != Integer(1)
assert Integer(1) > Integer(0)
assert Integer(1) >= Integer(1)

定义2:多重继承

mixin是一种设计模式,其中基类的一些方法使用了它没有定义的方法,并且该方法应该由另一个基类实现,而不是像定义1中那样由派生类实现。

术语mixin类指的是打算在该设计模式中使用的基类(TODO是使用该方法的基类,还是实现该方法的基类?)

判断一个给定的类是否为mixin并不容易:方法可以只是在派生类上实现,在这种情况下,我们回到定义1。你必须考虑作者的意图。

这种模式很有趣,因为它可以用不同的基类选择重新组合功能:

class HasMethod1(object):
    def method(self):
        return 1

class HasMethod2(object):
    def method(self):
        return 2

class UsesMethod10(object):
    def usesMethod(self):
        return self.method() + 10

class UsesMethod20(object):
    def usesMethod(self):
        return self.method() + 20

class C1_10(HasMethod1, UsesMethod10): pass
class C1_20(HasMethod1, UsesMethod20): pass
class C2_10(HasMethod2, UsesMethod10): pass
class C2_20(HasMethod2, UsesMethod20): pass

assert C1_10().usesMethod() == 11
assert C1_20().usesMethod() == 21
assert C2_10().usesMethod() == 12
assert C2_20().usesMethod() == 22

# Nothing prevents implementing the method
# on the base class like in Definition 1:

class C3_10(UsesMethod10):
    def method(self):
        return 3

assert C3_10().usesMethod() == 13

权威的Python事件

在官方的收藏文档展上。abc的文档明确使用术语Mixin方法。

它指出,如果一个类:

实现__next__ 继承自单个类Iterator

然后该类免费获得一个__iter__ mixin方法。

因此,至少在文档的这一点上,mixin不需要多重继承,并且与定义1一致。

文档在不同的地方当然可能是矛盾的,其他重要的Python库可能在它们的文档中使用其他定义。

本页还使用了术语Set mixin,这清楚地表明像Set和Iterator这样的类可以称为mixin类。

其他语言

Ruby:很明显,mixin不需要多重继承,就像主要的参考书如Programming Ruby和The Ruby Programming Language中提到的那样 c++:设为=0的虚方法是纯虚方法。 定义1与抽象类(具有纯虚方法的类)的定义一致。 该类不能被实例化。 定义2可以通过虚拟继承实现:两个派生类的多重继承

也许ruby中的一个例子会有所帮助:

你可以包含mixin Comparable并定义一个函数"<=>(other)", mixin提供了所有这些函数:

<(other)
>(other)
==(other)
<=(other)
>=(other)
between?(other)

它通过调用<=>(other)并返回正确的结果来实现这一点。

"instance <=> other"如果两个对象相等则返回0,如果instance大于other则返回小于0,如果other大于0则返回大于0。

首先,您应该注意,mixin只存在于多继承语言中。你不能在Java或c#中做mixin。

基本上,mixin是一个独立的基类型,为子类提供有限的功能和多态共振。如果你在用c#思考,考虑一个你不需要实际实现的接口,因为它已经实现了;你只是继承了它,并从它的功能中受益。

mixin的范围通常很窄,不需要扩展。

[编辑——至于为什么:]

既然你问了,我想我该解释一下原因。最大的好处是你不需要自己一遍又一遍地做。在c#中,mixin最大的好处可能来自于处置模式。无论何时实现IDisposable,您几乎总是希望遵循相同的模式,但最终要编写和重写相同的基本代码,只是略有变化。如果有一个可扩展的处置mixin,您可以节省大量额外的输入。

[编辑2 -回答你的其他问题]

mixin与多重继承的区别是什么?这只是语义上的问题吗?

是的。mixin和标准多重继承之间的区别只是语义上的问题;具有多重继承的类可以利用mixin作为多重继承的一部分。

mixin的目的是创建一个可以通过继承“混合”到任何其他类型的类型,而不影响继承类型,同时仍然为该类型提供一些有益的功能。

同样,考虑一个已经实现的接口。

我个人不使用mixins,因为我主要是用一种不支持mixins的语言开发的,所以我很难想出一个像样的例子来为您提供“啊哈!”的时刻。但我会再试一次。我将使用一个人为设计的示例——大多数语言已经以某种方式提供了该特性——但希望这将解释mixin应该如何创建和使用。是:

Suppose you have a type that you want to be able to serialize to and from XML. You want the type to provide a "ToXML" method that returns a string containing an XML fragment with the data values of the type, and a "FromXML" that allows the type to reconstruct its data values from an XML fragment in a string. Again, this is a contrived example, so perhaps you use a file stream, or an XML Writer class from your language's runtime library... whatever. The point is that you want to serialize your object to XML and get a new object back from XML.

本例中另一个重要的一点是,您希望以通用的方式执行此操作。你不希望为你想要序列化的每一个类型都实现一个“ToXML”和“FromXML”方法,你需要一些通用的方法来确保你的类型能够做到这一点,并且它只是工作。您希望代码重用。

如果您的语言支持它,您可以创建XmlSerializable mixin来完成您的工作。这种类型将实现ToXML和FromXML方法。它将使用一些对示例不重要的机制,能够从混合在其中的任何类型收集所有必要的数据,以构建由ToXML返回的XML片段,并且在调用FromXML时同样能够恢复该数据。

和. .就是这样。要使用它,您需要从XmlSerializable继承任何需要序列化为XML的类型。每当需要序列化或反序列化该类型时,只需调用ToXML或FromXML即可。事实上,由于XmlSerializable是一种成熟的多态类型,因此可以想象,您可以构建一个文档序列化器,它对原始类型一无所知,只接受一个XmlSerializable类型数组。

现在想象一下将这个场景用于其他事情,比如创建一个mixin,以确保每个混合它的类都记录每个方法调用,或者为混合它的类型提供事务性的mixin。这样的例子不胜枚举。

如果您只是将mixin视为一个小型的基本类型,用于在不影响类型的情况下向类型添加少量功能,那么您就很好了。

希望。:)