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

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

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

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


当前回答

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

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

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

其他回答

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

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

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

问候 斯特凡

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

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——它们是类似的概念,但在使用和实现上有所不同。

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

mixin是一种有限形式的多重继承。在某些语言中,向类中添加mixin的机制(就语法而言)与继承的机制略有不同。

特别是在Python上下文中,mixin是一个父类,它为子类提供功能,但并不打算自己被实例化。

你可能会说,“这只是多重继承,而不是真正的mixin”,因为可能被混淆为mixin的类实际上可以被实例化和使用——所以这确实是语义上的,而且非常真实的区别。

多重继承示例

这个例子,来自文档,是一个OrderedCounter:

类OrderedCounter(计数器,OrderedDict): “计数器,记住第一次遇到的顺序元素” def __repr__(自我): 返回'%s(%r)' % (self.__class__. zip)。__name__ OrderedDict(自我) def __reduce__(自我): 回归自我。__class__进行(OrderedDict(自我),)

它从集合模块继承了Counter和OrderedDict。

Counter和OrderedDict都打算被实例化并单独使用。然而,通过将它们都子类化,我们可以拥有一个有序的计数器,并重用每个对象中的代码。

这是一种重用代码的强大方法,但也可能产生问题。如果发现其中一个对象存在bug,那么不加注意地修复它可能会在子类中创建一个bug。

Mixin的例子

mixin通常被推广为一种获得代码重用的方式,而不会出现协作多重继承(如OrderedCounter)可能存在的潜在耦合问题。当您使用mixin时,您使用的功能与数据不是紧密耦合的。

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

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

可以创建每种类型服务器的分叉和线程版本 使用这些混合类。例如,ThreadingUDPServer是 创建如下: 类ThreadingMixIn, UDPServer: 通过 mix-in类首先出现,因为它覆盖了定义在 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()

一个虚构的例子

这是一个主要用于演示目的的mixin -大多数对象将会进化到超出这个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)

这不是一个Python的例子,但在D编程语言中,术语mixin是用来指一个以同样方式使用的结构;向类中添加一堆东西。

在D中(顺便提一下,它不做MI),这是通过将一个模板(考虑语法意识和安全宏,您将接近)插入一个作用域来完成的。这允许在类、结构、函数、模块或任何东西中的一行代码扩展为任意数量的声明。

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