什么是混合,为什么他们有用?

在“ Programming Python ”中,Mark Lutz提到了“mixins”。 我来自C / C ++ / C#的背景,我以前没有听说过这个词。 什么是混合?

阅读这个例子 (我已经链接到,因为它很长)之间的线,我假设这是一个使用多inheritance来扩展一个类,而不是'适当'的子类。 这是正确的吗?

我为什么要这样做,而不是把新的function放到一个子类中? 就此而言,为什么mixin / multiple inheritance方法比使用composition更好?

什么将混合与多重inheritance分开? 这只是一个语义问题吗?

mixin是一种特殊的多重inheritance。 混合使用有两种主要情况:

  1. 你想为一个类提供许多可选的function。
  2. 你想在许多不同的类中使用一个特定的function。

举一个例子,考虑werkzeug的请求和响应系统 。 我可以说一个普通的旧请求对象:

from werkzeug import BaseRequest class Request(BaseRequest): pass 

如果我想添加接受头支持,我会做到这一点

 from werkzeug import BaseRequest, AcceptMixin class Request(BaseRequest, AcceptMixin): pass 

如果我想创build一个支持accept header,etags,authentication和user agent支持的请求对象,我可以这样做:

 from werkzeug import BaseRequest, AcceptMixin, ETagRequestMixin, UserAgentMixin, AuthenticationMixin class Request(BaseRequest, AcceptMixin, ETagRequestMixin, UserAgentMixin, AuthenticationMixin): pass 

这个差别是微妙的,但是在上面的例子中,混合类并不是独立存在的。 在更传统的多重inheritance中, AuthenticationMixin (例如)可能更像Authenticator 。 也就是说,这个class级可能会被devise成独立的。

首先,你应该注意到mixin只存在于多inheritance语言中。 你不能在Java或C#中进行混音。

基本上,mixin是一个独立的基本types,为儿童课程提供有限的function和多态共鸣。 如果你想用C#,想一下你不必实际实现的接口,因为它已经实现了; 你只是从它inheritance,并从其function中受益。

Mixins的范围通常很窄,并不意味着延长。

[编辑 – 至于为什么:]

我想我应该解释为什么,因为你问。 最大的好处就是你不必一遍又一遍地自己做。 在C#中,mixin最大的好处可能来自于Disposal模式 。 每当你实现IDisposable的时候,你几乎总是想要遵循相同的模式,但是最终你会写出和重写相同的基本代码。 如果有一个可扩展的处置混合,你可以节省自己很多额外的打字。

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

什么将混合与多重inheritance分开? 这只是一个语义问题吗?

是。 mixin和标准多重inheritance之间的区别只是一个语义问题; 一个具有多重inheritance的类可能会使用一个mixin作为多重inheritance的一部分。

mixin的意义在于创build一个可以通过inheritance“混合”到任何其他types的types,而不会影响inheritancetypes,同时仍然为该types提供一些有益的function。

再次想一下已经实现的接口。

我个人不使用mixins,因为我主要是用一种不支持他们的语言来开发,所以我很难想出一个体面的例子来提供这个“ahah!”。 给你的时刻。 但我会再试一次。 我将使用一个人为devise的例子 – 大多数语言已经以某种方式提供了这个function,但希望能解释mixin是如何被创build和使用的。 开始:

假设你有一个你希望能够从XML序列化的types。 您希望该types提供一个“ToXML”方法,该方法返回一个包含具有该types数据值的XML片段的string,以及一个允许types从string中的XML片段重build其数据值的“FromXML”。 再次,这是一个人为的例子,所以也许你使用一个文件stream,或从你的语言的运行时库中的XML编写器类…无论如何。 重点是你想要序列化你的对象到XML,并从XML返回一个新的对象。

在这个例子中的另一个重要的一点是,你想以通用的方式做到这一点。 你不想为每一个你想要序列化的types都实现一个“ToXML”和“FromXML”方法,你需要一些通用的方法来确保你的types能够做到这一点,它只是工作。 你想要重复使用代码。

如果你的语言支持它,你可以创buildXmlSerializable混搭为你做你的工作。 这种types将实现ToXML和FromXML方法。 如果使用一些对本示例不重要的机制,就可以从任何types中收集所有必要的数据,以便构buildToXML返回的XML片段,并且在FromXML为调用。

而..就是这样。 要使用它,你将有任何需要被序列化为从XmlSerializableinheritance的XML的types。 无论何时需要序列化或反序列化该types,都可以简单地调用ToXML或FromXML。 事实上,由于XmlSerializable是一个完全成熟的types和多态,你可以想象,构build一个文档序列化程序,它不知道任何关于你的原始types,只接受一个XmlSerializabletypes的数组。

现在想象一下使用这个场景来做其他的事情,比如创build一个混入器,确保每个混合它的类都logging每个方法的调用,或者为混合types提供事务性的混入器。列表可以继续。

如果你只是想把一个mixin作为一个小型的基础types来devise,为一个types添加less量的function,而不会影响这个types,那么你就是个好主意。

希望。 🙂

这个答案旨在解释mixins 的例子

  • 自成一体 :简短,无需了解任何图书馆的例子。

  • 在Python中 ,不在其他语言中。

    可以理解的是,有其他语言例如Ruby的例子,因为这个术语在这些语言中更为常见,但这是一个Python线程。

还应考虑有争议的问题:

多重inheritance是否需要表征一个mixin?

定义

我还没有看到来自“权威”来源的引用,清楚地说明了什么是Python中的混合。

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

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

定义1:没有多重inheritance

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

所以这个类不是要被实例化,而是作为一个基类。 否则,该实例将有方法,不会引发exception无法调用。

一些源添加的约束是该类可能不包含数据,只有方法,但我不明白为什么这是必要的。 然而在实践中,许多有用的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_ordering()装饰器来实现,但是这里的游戏是重新发明轮子的:

 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:多重inheritance

mixin是一种devise模式,其中基类的某些方法使用了它没有定义的方法,并且该方法意味着由另一个基类实现 ,而不是由定义1中的派生来实现。

术语mixin类指的是打算在该devise模式中使用的基类(TODO使用该方法的那些或实现它的那些?)

要确定一个给定的类是否是一个混合,并不容易:该方法可以在派生类上实现,在这种情况下,我们回到定义1中。您必须考虑作者的意图。

这种模式很有趣,因为可以用不同的基类select重新组合function:

 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事件

在正式文件中为collections.abc文档明确地使用术语Mixin方法

它指出,如果一个类:

  • 实现__next__
  • 从单个类Iteratorinheritance

那么这个类可以免费获得__iter__ mixin方法

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

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

这个页面还使用术语Set mixin ,这清楚地表明像SetIterator这样的类可以称为Mixin类。

在其他语言

  • Ruby:显然不需要对mixin进行多重inheritance,正如主要参考书(如Programming Ruby和Ruby编程语言)中提到的那样

  • C ++:一个没有实现的方法是一个纯粹的虚拟方法。

    定义1符合抽象类(具有纯虚拟方法的类)的定义。 那个类不能被实例化。

    定义2可以通过虚拟inheritance来实现: 来自两个派生类的多inheritance

我认为它们是使用多重inheritance的一种有纪律的方式 – 因为最终mixin只是另一个可能遵循有关称为mixin的类的约定的python类。

我对约定的理解,你会称之为Mixin的是一个Mixin:

  • 添加方法,但不是实例variables(类常量是确定的)
  • 只从objectinheritance(在Python中)

这样就限制了多重inheritance的潜在复杂性,并且通过限制你必须查看的地方(与完全多重inheritance相比),可以很容易地跟踪程序的stream程。 他们类似于ruby模块。

如果我想添加实例variables(比单inheritance允许更多的灵活性),那么我倾向于合成。

话虽如此,我已经看到了名为XYZMixin的类有实例variables。

Mixins是Programming中的一个概念,其中类提供了function,但并不意味着用于实例化。 Mixin的主要目的是提供独立的function,如果mixin本身不具有与其他mixin的inheritance关系并且也避免状态,那将是最好的。 在诸如Ruby这样的语言中,有一些直接的语言支持,但是对于Python而言,并没有。 但是,您可以使用多类inheritance来执行Python提供的function。

我观看了此videohttp://www.youtube.com/watch?v=v_uKI2NOLEM以了解mixin的基础知识。 初学者理解mixin的基本知识,以及它们如何工作以及在实现它们时可能遇到的问题是非常有用的。

维基百科仍然是最好的: http : //en.wikipedia.org/wiki/Mixin

我build议不要在新的Python代码中混入,如果你可以find任何其他的方法(比如inheritance的组合而不是inheritance,或者只是将猴子修补到你自己的类中)努力。

在旧式课堂中,您可以使用混合插件作为从另一个class级获取几种方法的方式。 但是在新风格的世界里,所有的东西,甚至是混入,从object中inheritance下来。 这意味着任何多重inheritance的使用自然会引入MRO问题 。

有很多方法可以使Python中的多重inheritanceMRO工作,尤其是super()函数,但这意味着你必须使用super()来完成整个类的层次结构,而理解控制stream程则相当困难。

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

在Scala中,你可以像这里所描述的那样做mixin,但是有趣的是,mixin实际上是“融合”在一起创build一个新的类inheritance的。 从本质上说,你不能从多个类/混合类inheritance,而是要生成一个新类,该类需要inheritance自混合类的所有属性。 这是有道理的,因为Scala是基于JVM的,当前不支持多inheritance(从Java 8开始)。 顺便说一下,这个mixin类types是一个特殊的types,称为斯卡拉的特质。

它被暗示在一个类被定义的方式:类NewClass扩展FirstMixin与SecondMixin ThirdMixin …

我不确定CPython解释器是否也是一样的(mixin class-composition),但我不会感到惊讶。 另外,来自C ++的背景,我不会称之为ABC或与mixin相当的“界面” – 这是一个类似的概念,但在使用和实现方面有所不同。

什么将混合与多重inheritance分开? 这只是一个语义问题吗?

mixin是多重inheritance的有限forms。 在某些语言中,将mixin添加到类中的机制与inheritance的机制略有不同(在语法上)。

特别是在Python的上下文中,mixin是一个父类,为子类提供function,但不打算自行实例化。

什么可能会导致你说,“这只是多重inheritance,而不是一个混合”是,如果可能混淆为一个mixin类可以实例化和使用 – 所以它确实是一个语义,非常真实的差异。

多重inheritance的例子

这个例子, 从文档 ,是OrderedCounter:

 class OrderedCounter(Counter, OrderedDict): 'Counter that remembers the order elements are first encountered' def __repr__(self): return '%s(%r)' % (self.__class__.__name__, OrderedDict(self)) def __reduce__(self): return self.__class__, (OrderedDict(self),) 

它将来自collections模块的CounterOrderedDict子类化。

CounterOrderedDict都是要实例化和自己使用的。 但是,通过对它们进行子类化,我们可以有一个有序的计数器,并重用每个对象中的代码。

这是重用代码的有效方式,但也可能会产生问题。 如果发现其中一个对象存在错误,则无需修复就可以在子类中创build一个错误。

Mixin的例子

Mixins通常被提升为获得代码重用的方式,而没有像OrderedCounter那样的协作多重inheritance的潜在耦合问题。 当您使用mixin时,您使用的function与数据紧密结合。

与上面的例子不同,mixin并不打算单独使用。 它提供了新的或不同的function。

例如,标准库在socketserver库中有几个mixin 。

每种服务器的分叉和线程版本都可以使用这些混合类来创build。 例如,ThreadingUDPServer创build如下:

 class ThreadingUDPServer(ThreadingMixIn, UDPServer): pass 

混合类是第一个,因为它覆盖了UDPServer中定义的方法。 设置各种属性也会改变底层服务器机制的行为。

在这种情况下,mixin方法会覆盖UDPServer对象定义中的方法以允许并发。

重写的方法似乎是process_request ,它也提供了另一个方法process_request_thread 。 这里是从源代码 :

 class ThreadingMixIn: """Mix-in class to handle each request in a new thread.""" # Decides how threads will act upon termination of the # main process daemon_threads = False def process_request_thread(self, request, client_address): """Same as in BaseServer but as a thread. In addition, exception handling is done here. """ try: self.finish_request(request, client_address) except Exception: self.handle_error(request, client_address) finally: self.shutdown_request(request) def process_request(self, request, client_address): """Start a new thread to process the request.""" t = threading.Thread(target = self.process_request_thread, args = (request, client_address)) t.daemon = self.daemon_threads t.start() 

一个受挫的例子

这是一个混合,主要是为了演示目的 – 大多数对象将演变超出这个repr的有用性:

 class SimpleInitReprMixin(object): """mixin, don't instantiate - useful for classes instantiable by keyword arguments to their __init__ method. """ __slots__ = () # allow subclasses to use __slots__ to prevent __dict__ def __repr__(self): kwarg_strings = [] d = getattr(self, '__dict__', None) if d is not None: for k, v in d.items(): kwarg_strings.append('{k}={v}'.format(k=k, v=repr(v))) slots = getattr(self, '__slots__', None) if slots is not None: for k in slots: v = getattr(self, k, None) kwarg_strings.append('{k}={v}'.format(k=k, v=repr(v))) return '{name}({kwargs})'.format( name=type(self).__name__, kwargs=', '.join(kwarg_strings) ) 

用法是:

 class Foo(SimpleInitReprMixin): # add other mixins and/or extend another class here __slots__ = 'foo', def __init__(self, foo=None): self.foo = foo super(Foo, self).__init__() 

和用法:

 >>> f1 = Foo('bar') >>> f2 = Foo() >>> f1 Foo(foo='bar') >>> f2 Foo(foo=None) 

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

如果您正在构build一个类,并且希望它像字典一样工作,则可以定义所有必需的各种__ __方法。 但是这有点痛苦。 作为一个替代方法,你可以定义一些,并从UserDict.DictMixininheritance(除了任何其他的inheritance)(移到py3k中的collections.DictMixin )。 这将会自动定义字典api的其余部分。

第二个例子:GUI工具包wxPython允许你使用多个列进行列表控件(比如说Windows资源pipe理器中的文件显示)。 默认情况下,这些列表是相当基本的。 您可以添加其他function,例如通过单击列标题,通过从ListCtrlinheritance并添加适当的mixins,按特定列sorting列表的function。

也许从ruby的例子可以帮助:

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

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

它通过调用<=>(other)并返回正确的结果。

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

这不是一个Python的例子,但是在D编程语言中 , mixin这个术语用来指代一个使用非常相似的构造; 给课堂增加一堆东西。

在D中(顺便说一句,这不是MI),这是通过插入一个模板(认为语法意识和安全的macros,你会接近)到一个范围内完成的。 这允许在一个类,结构,函数,模块或任何其他的任何扩展到任何数量的声明的单行代码。

mixin提供了在类中添加function的方法,也就是说,您可以通过将模块包含在所需的类中来与模块中定义的方法进行交互。 尽piperuby不支持多重inheritance,但是提供了mixin作为实现这一点的替代scheme。

这里是一个例子,说明如何使用mixin实现多重inheritance。

 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 

我只是用python mixin来为python milters实现unit testing。 通常情况下,一个MTA与MTA进行交谈,使得unit testing变得困难。 testingmixin覆盖了与MTA交谈的方法,而是创build一个由testing用例驱动的模拟环境。

所以,你需要一个未经修改的milter应用程序,如spfmilter和mixin TestBase,如下所示:

 class TestMilter(TestBase,spfmilter.spfMilter): def __init__(self): TestBase.__init__(self) spfmilter.config = spfmilter.Config() spfmilter.config.access_file = 'test/access.db' spfmilter.spfMilter.__init__(self) 

然后,在Milter应用程序的testing用例中使用TestMilter:

 def testPass(self): milter = TestMilter() rc = milter.connect('mail.example.com',ip='192.0.2.1') self.assertEqual(rc,Milter.CONTINUE) rc = milter.feedMsg('test1',sender='good@example.com') self.assertEqual(rc,Milter.CONTINUE) milter.close() 

http://pymilter.cvs.sourceforge.net/viewvc/pymilter/pymilter/Milter/test.py?revision=1.6&view=markup

我读过你有ac#背景。 所以一个好的起点可能是.NET的混合实现。

您可能需要查看http://remix.codeplex.com/上的codeplex项目;

观看lang.net Symposium链接以获得概述。 还有更多关于codeplex页面上的文档。

关于斯蒂芬

Re @ Ciro Santilli的回答 :实际上可以使用C ++中的混合使用接口的第二个定义

 #include <iostream> struct Interface { virtual int method() const = 0; }; class HasMethod1 : public virtual Interface { int method() const {return 1;} }; class HasMethod2 : public virtual Interface { int method() const {return 2;} }; class UsesMethod10 : public virtual Interface { public: int usesMethod() const {return 10+method();} }; class UsesMethod20 : public virtual Interface { public: int usesMethod() const {return 20+method();} }; template <typename T1, typename T2> struct Combination : public T1, public T2 { }; int main() { std::cout << Combination<HasMethod1, UsesMethod10>().usesMethod() << std::endl; std::cout << Combination<HasMethod2, UsesMethod10>().usesMethod() << std::endl; std::cout << Combination<HasMethod1, UsesMethod20>().usesMethod() << std::endl; std::cout << Combination<HasMethod2, UsesMethod20>().usesMethod() << std::endl; return 0; } 

使用CRTP的forms可能会更好,但我不打算在这里过分。

OP提到他/她从来没有听说过C ++中的mixin,也许是因为他们在C ++中被称为好奇的循环模板模式(CRTP)。 另外,@Ciro Santilli提到mixin是通过C ++中的抽象基类实现的。 尽pipe抽象基类可以用来实现mixin,但由于虚拟函数在运行时的function可以在编译时使用模板来实现,而无需在运行时查找虚拟表的开销,所以这是一种矫枉过正。

CRTP模式在这里详细描述

我已经将@Ciro Santilli的答案中的python示例转换为使用以下模板类的C ++:

 #include <iostream> #include <assert.h> template <class T> class ComparableMixin { public: bool operator !=(ComparableMixin &other) { return ~(*static_cast<T*>(this) == static_cast<T&>(other)); } bool operator <(ComparableMixin &other) { return ((*(this) != other) && (*static_cast<T*>(this) <= static_cast<T&>(other))); } bool operator >(ComparableMixin &other) { return ~(*static_cast<T*>(this) <= static_cast<T&>(other)); } bool operator >=(ComparableMixin &other) { return ((*static_cast<T*>(this) == static_cast<T&>(other)) || (*(this) > other)); } }; class Integer: public ComparableMixin<Integer> { public: Integer(int i) { this->i = i; } int i; bool operator <=(Integer &other) { return (this->i <= other.i); } bool operator ==(Integer &other) { return (this->i == other.i); } }; int main() { Integer i(0) ; Integer j(1) ; assert (i < j ); assert (i != j); assert (j > i); assert (j >= i); return 0; }