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

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

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

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


当前回答

这个答案旨在通过以下例子解释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可以通过虚拟继承实现:两个派生类的多重继承

其他回答

我认为它们是使用多重继承的一种有纪律的方式——因为最终mixin只是另一个python类,它(可能)遵循称为mixin的类的约定。

我对Mixin的理解是:

添加方法但不添加实例变量(类常量是可以的) 仅从object继承(Python中)

通过这种方式,它限制了多重继承的潜在复杂性,并通过限制必须查看的位置(与完整的多重继承相比)使跟踪程序流程变得相当容易。它们类似于ruby模块。

如果我想添加实例变量(具有比单继承所允许的更大的灵活性),那么我倾向于组合。

话虽如此,我还见过名为XYZMixin的类确实有实例变量。

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构造函数将如何导致编译错误。

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

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

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

这个概念来自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'

大致总结一下上面所有的好答案:

                States        /     Methods Concrete Method Abstract Method
Concrete State Class Abstract Class
Abstract State Mixin Interface