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

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

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

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


当前回答

这个概念来自Steve 's Ice Cream,这是一家由Steve Herrell于1973年在马萨诸塞州萨默维尔创立的冰淇淋店,在那里混合(糖果、蛋糕等)被混合成基本的冰淇淋口味(香草、巧克力等)。

受到Steve 's Ice Cream的启发,Lisp对象系统Flavors的设计者第一次在编程语言中包含了这个概念,其中mix-in是用于增强其他类的小型辅助类,flavour是大型独立类。

因此,主要的思想是mix-in是一个可重用的扩展(“可重用”而不是“独占”;' extension '而不是' base ')。

这个概念与单一或多个继承以及抽象或具体类的概念是正交的。混合类可以用于单个或多个继承,可以是抽象类也可以是具体类。混合类有不完整的接口,抽象类有不完整的实现,具体类有完整的实现。

Mix-in类名通常以' -MixIn '、' -able '或' -ible '作为后缀,以强调它们的性质,就像Python标准库中socketserver模块的ThreadingMixIn和ForkingMixIn类,以及集合的Hashable、Iterable、Callable、Awaitable、AsyncIterable和可逆类一样。美国广播公司(abc)模块。

下面是一个mix-in类的例子,用于扩展Python内置list和dict类的日志记录功能:

import logging

class LoggingMixIn:
    def __setitem__(self, key, value):
        logging.info('Setting %r to %r', key, value)
        super().__setitem__(key, value)
    def __delitem__(self, key):
        logging.info('Deleting %r', key)
        super().__delitem__(key)

class LoggingList(LoggingMixIn, list):
    pass

class LoggingDict(LoggingMixIn, dict):
    pass
>>> logging.basicConfig(level=logging.INFO)
>>> l = LoggingList([False])
>>> d = LoggingDict({'a': False})
>>> l[0] = True
INFO:root:Setting 0 to True
>>> d['a'] = True
INFO:root:Setting 'a' to True
>>> del l[0]
INFO:root:Deleting 0
>>> del d['a']
INFO:root:Deleting 'a'

其他回答

我读到你有c#背景。因此,一个好的起点可能是。net的mixin实现。

你可能想在http://remix.codeplex.com/上查看codeplex项目

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

问候 斯特凡

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

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

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

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

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

我建议不要在新的Python代码中使用mix-in,如果你能找到其他方法(比如用复合代替继承,或者只是把方法打补丁到你自己的类中),这样做并不费力。

在老式的类中,您可以使用mix- In作为从另一个类中抓取一些方法的一种方式。但在新风格的世界里,一切,甚至是混合,都继承自对象。这意味着任何多重继承的使用都会自然地引入MRO问题。

在Python中有很多方法可以使多继承MRO工作,最著名的是super()函数,但这意味着您必须使用super()来完成整个类层次结构,并且要理解控制流要困难得多。

OP提到他/她从未听说过c++中的mixin,可能是因为它们在c++中被称为奇怪的重复模板模式(CRTP)。另外,@Ciro Santilli提到,mixin在c++中是通过抽象基类实现的。虽然可以使用抽象基类来实现mixin,但这是一种过度使用,因为运行时的虚函数功能可以在编译时使用模板来实现,而不需要在运行时查找虚表。

这里将详细描述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));
        }
        protected:
            ComparableMixin() {}
    };

    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) ;
    //ComparableMixin<Integer> c; // this will cause compilation error because constructor is protected.
    assert (i < j );
    assert (i != j);
    assert (j >  i);
    assert (j >= i);

    return 0;
}

编辑:在ComparableMixin中添加了受保护的构造函数,这样它只能被继承而不能被实例化。更新了示例,以显示在创建ComparableMixin对象时protected构造函数将如何导致编译错误。

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