什么是mixin,为什么它们有用?


951

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

本示例的两行之间进行阅读(我已经链接到它,因为它很长),我认为这是使用多重继承来扩展类而不是“适当的”子类的一种情况。这是正确的吗?

为什么我要这样做而不是将新功能放入子类中?因此,为什么混合/多重继承方法比使用组合更好?

什么将mixin与多重继承分开?这仅仅是语义问题吗?

Answers:


706

mixin是一种特殊的多重继承。使用mixin的主要情况有两种:

  1. 您想为一个类提供很多可选功能。
  2. 您想在许多不同的类中使用一种特定功能。

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

from werkzeug import BaseRequest

class Request(BaseRequest):
    pass

如果我想添加接受标头支持,我会做

from werkzeug import BaseRequest, AcceptMixin

class Request(AcceptMixin, BaseRequest):
    pass

如果我想创建一个支持接受标头,etag,身份验证和用户代理支持的请求对象,则可以执行以下操作:

from werkzeug import BaseRequest, AcceptMixin, ETagRequestMixin, UserAgentMixin, AuthenticationMixin

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

区别是细微的,但是在上面的示例中,mixin类并不是独立存在的。在更传统的多重继承中,AuthenticationMixin(例如)可能更像Authenticator。也就是说,该类可能会设计为独立存在。


123
第三种情况是:您想为一个类提供很多(非可选)功能,但是您希望这些功能在单独的类(和单独的模块)中,因此每个模块都是一个功能(行为)。IOW,而不是供重复使用,但用于分隔。
bootchk 2013年

60
在此示例中可能不是问题,但是您通常希望将主基类作为括号中的最后一个元素,以便创建继承链:Request ==> Mixin ==> ... ==> BaseRequest。看到这里:ianlewis.org/en/mixins-and-python
hillel

10
@hillel是个好主意,但是请记住,Python将从左到右调用超类的方法(例如,当您需要重写构造函数时)。
Eliseu Monar dos Santos 2015年

9
这听起来很像Decorator设计模式。
D-Jones

4
第4个的情况是:已经有一个现有的家庭ParentChild1Child2ChildN第三方库里面的子类,并且要为整个家庭定制的行为。理想情况下,您希望将此类行为添加到中Parent,并希望第三方库开发人员将接受您的请求请求。否则,您将必须实现自己的class NewBehaviorMixin,然后定义一整套包装器类,例如class NewParent(NewBehaviorMixin, Parent): passclass NewChildN(NewBehaviorMixin, ChildN): pass等。(PS:您知道更好的方法吗?)
RayLuo

240

首先,您应该注意,mixin仅存在于多种继承语言中。您不能使用Java或C#进行混合。

基本上,mixin是独立的基本类型,可为子类提供有限的功能和多态共振。如果您正在考虑使用C#,请考虑一下您不必实际实现的接口,因为该接口已经实现了。您只需继承它并从其功能中受益。

Mixins通常范围狭窄,不打算扩展。

[编辑-关于原因:]

既然您问过,我想我应该说一下原因。最大的好处是您不必一遍又一遍地自己做。在C#中,mixin受益最大的地方可能是Disposal模式。每当实现IDisposable时,几乎总是希望遵循相同的模式,但最终会以较小的变化编写和重新编写相同的基本代码。如果有可扩展的Disposal mixin,则可以节省很多额外的键入操作。

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

什么将mixin与多重继承分开?这仅仅是语义问题吗?

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

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

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

我个人不使用mixins,因为我主要使用不支持它们的语言进行开发,因此我很难拿出一个像样的示例来提供“啊!”的好例子。你的时刻。但我会再试一次。我将使用一个人为设计的示例-大多数语言已经以某种方式提供了该功能-希望这将解释应该如何创建和使用mixin。开始:

假设您具有一个可以与XML进行序列化的类型。您希望该类型提供“ ToXML”方法,该方法返回包含具有该类型的数据值的XML片段的字符串,以及“ FromXML”,其允许该类型从字符串中的XML片段重建其数据值。同样,这是一个人为的示例,因此也许您使用文件流或语言运行时库中的XML Writer类...等等。关键是您想将对象序列化为XML并从XML取回新对象。

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

如果您的语言支持,则可以创建XmlSerializable mixin为您完成工作。此类型将实现ToXML和FromXML方法。它将使用对示例不重要的某种机制,能够从与之混合的任何类型中收集所有必要的数据,以构建ToXML返回的XML片段,并且当FromXML为叫。

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

现在想象一下将此场景用于其他用途,例如创建一个确保每个混合了它的类的mixin记录每个方法调用,或者一个为混合它的类型提供事务性的mixin。列表可以继续。

如果您只是将mixin视为旨在为类型添加少量功能而又不影响该类型的小型基本类型,那么您就是无所不能。

希望。:)


25
嘿,您喜欢这个短语“多态共振”吗?自己做。我认为。也许我在某个地方的物理学中听到了……
Randolpho

50
我对你的第一句话略有不同意见。Ruby是一种单继承语言,mixins是向不带其他类继承的给定类添加方法的方法。
凯尔蒂亚

23
@Keltia:我认为混合-从定义上讲是多重继承。在Ruby的情况下,它们是一个猴子补丁(或其他)而不是适当的混合。Ruby人们可能将其称为mixin,但这是另一回事。
S.Lott

10
实际上,真正的mixin不能使用多重继承。混合包含一个类中的一个类的方法,属性等,而不继承它。这往往会给代码重用带来多态性带来的好处,但是却排除了确定亲子关系的问题(死亡的钻石等)。Mixin支持语言也倾向于允许部分包含mixin类(事情开始听起来有点像方面)。
Trevor

8
作为记录,Java现在支持带有默认方法的mixins。
shmosel

169

该答案旨在通过以下示例解释mixin :

  • 自包含:简短,无需了解任何库即可理解示例。

  • 用Python而不是其他语言。

    可以理解,存在其他语言(例如Ruby)的示例,因为该术语在这些语言中更为常见,但这是Python线程。

它还应考虑有争议的问题:

是否需要多重继承来表征mixin?

定义

我还没有看到来自“权威”来源的引文,清楚地说明了Python中的mixin。

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

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

定义1:无多重继承

mixin是一个类,以便该类的某些方法使用该类中未定义的方法。

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

一些资料来源增加的一个约束是该类可能不包含数据,仅包含方法,但我不明白为什么这样做是必要的。但是实际上,许多有用的mixin没有任何数据,并且没有数据的基类更易于使用。

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

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:多重继承

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

术语“ 混合类”是指打算在该设计模式中使用的基类(使用方法的那些类是TODO,还是实现该方法的那些?

决定给定类是否为混合类并不容易:该方法可以仅在派生类上实现,在这种情况下,我们回到定义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事件

collections.abc官方文档中,该文档明确使用术语Mixin Methods

它指出如果一个类:

  • 贯彻 __next__
  • 从单个类继承 Iterator

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

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

当然,文档在不同点上可能是矛盾的,并且其他重要的Python库可能正在其文档中使用其他定义。

该页面还使用术语Set mixin,它明确表明类似类Set并且Iterator可以称为Mixin类。

用其他语言

  • 红宝石:显然不需要混入多重继承,如主要参考书如提到的编程的Ruby和Ruby编程语言

  • C ++:未实现的方法是纯虚拟方法。

    定义1与抽象类(具有纯虚方法的类)的定义一致。该类无法实例化。

    虚拟继承可以定义2:来自两个派生类的多重继承


37

我认为它们是使用多重继承的一种有条理的方式-因为mixin最终只是(可能)遵循关于被称为mixin的类的约定的另一个python类。

我对管理您称为Mixin的约定的理解是Mixin:

  • 添加方法但不添加实例变量(类常量可以)
  • 仅继承自object(在Python中)

这样,它限制了多重继承的潜在复杂性,并且通过限制外观(相对于完全多重继承),使跟踪程序流变得相当容易。它们类似于ruby模块

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

话虽如此,我看到了名为XYZMixin的类,它们确实具有实例变量。


30

Mixins是“编程”中的一个概念,其中该类提供功能,但并不用于实例化。Mixins的主要目的是提供独立的功能,并且最好的是,mixin本身不与其他mixin继承并且也避免状态。在诸如Ruby之类的语言中,有一些直接的语言支持,但对于Python则没有。但是,您可以使用多类继承来执行Python中提供的功能。

我观看了http://www.youtube.com/watch?v=v_uKI2NOLEM的视频,以了解Mixins的基础知识。对于初学者来说,了解mixin的基础知识,它们如何工作以及在实现它们时可能遇到的问题非常有用。

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


29

什么将mixin与多重继承分开?这仅仅是语义问题吗?

混合是多重继承的有限形式。在某些语言中,将mixin添加到类的机制(在语法方面)与继承略有不同。

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

您可能会说,“那只是多重继承,而不是真正的mixin”是因为实际上可以实例化和使用对于mixin感到困惑的类,因此,这确实是语义上的,而且非常真实。

多重继承的例子

该示例来自文档,是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),)

它从模块子类化Counter和。OrderedDictcollections

双方CounterOrderedDict意图被实例化,并在自己使用。但是,通过将它们都子类化,我们可以得到一个有序的计数器,并在每个对象中重用代码。

这是重用代码的有效方法,但也可能会出现问题。如果事实证明其中一个对象中存在错误,则不小心修复它可能会在子类中创建错误。

混合的例子

通常将Mixins提倡为获得代码重用的方式,而又避免了诸如OrderedCounter之类的协作多重继承可能存在的潜在耦合问题。当您使用mixins时,您使用的功能与数据紧密耦合。

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

例如,标准库有一对夫妇在混入socketserver

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

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()

人为的例子

这是一个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)

11

我认为这里有一些很好的解释,但我想提供另一种观点。

在Scala中,您可以执行混合操作,如此处所述,但非常有趣的是,混合操作实际上是“融合”在一起的,以创建一种新的继承类。本质上,您不是从多个类/ mixins继承,而是生成一种具有mixin所有属性的新类。这是有道理的,因为Scala基于JVM(目前不支持多重继承)(从Java 8开始)。顺便说一下,这种mixin类类型是一种特殊类型,在Scala中称为Trait。

它以类定义的方式提示:类NewClass扩展FirstMixin和SecondMixin以及ThirdMixin ...

我不确定CPython解释器是否执行相同的操作(mixin类组成),但是我不会感到惊讶。同样,来自C ++背景,我不会将ABC或“接口”等同于mixin,它是一个相似的概念,但是在使用和实现上存在分歧。


9

我建议不要在新的Python代码中进行混入,如果您能找到其他解决方法(例如,代替合成的继承关系,或者只是在自己的类中使用猴子修补方法),那就不多了努力。

在老式类中,您可以使用混入作为从另一个类中获取一些方法的一种方式。但是在新式世界中,即使是混入,一切也都继承自object。这意味着对多重继承的任何使用自然会引入MRO问题

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


3
从2.3版开始,Python使用了Python 2.3方法分辨率顺序方法分辨率顺序中说明的“ C3方法分辨率” 。
webwurst 2012年

11
就我个人而言,在大多数情况下,我会选择mixins代替猴子补丁。可以更轻松地进行推理和遵循代码。
tdammers

5
不赞成投票。尽管您的答案表达了对开发风格的正确看法,但您并未真正解决实际的问题。
Ryan B. Lynch 2013年

8

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

如果您要构建一个类并希望它像字典一样工作,则可以定义所有__ __必要的方法。但这有点痛苦。作为替代方案,您可以只定义一些,并从UserDict.DictMixin(继承自collections.DictMixinpy3k中)继承(除了任何其他继承)。这将具有自动定义其余所有字典api的作用。

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


8

这不是Python的示例,但在D编程语言中,该术语mixin用于指代使用几乎相同方式的构造。在课堂上添加一堆东西。

在D中(顺便说一下,它不执行MI),这是通过将一个模板(认为具有语法意识和安全的宏,您将接近)插入一个范围来完成的。这允许在类,结构,函数,模块或任何可以扩展为任意数量的声明的代码中使用一行代码。


2
混入是一个总称,在d,红宝石等,根据维基百科,它们起源于老同学口齿不清系统中使用,并于1983年被首次记载:en.wikipedia.org/wiki/...
利B

7

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对象时受保护的构造函数将如何导致编译错误。


Mixins和CRTP在C ++中并不是完全一样的。
ashrasmun

6

也许来自ruby的示例可以帮助您:

您可以包括mixin Comparable并定义一个功能"<=>(other)",mixin提供所有这些功能:

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

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

"instance <=> other"返回0,如果两个对象是相等的,小于0,如果instance是比更大other和超过0,如果other是更大的。


是为Python提供类似mixin的文章。尽管此建议的定义__lt__是而不是基础__cmp__,但实际上不建议使用后者,不建议使用。对我来说,似乎更容易使用的是混入,而不是非常复杂的装饰(部分functools) -尽管这其中可能能够更动态地反应在提供的比较...
托比亚斯Kienzler

6

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

4
通常,此继承与多重继承有什么区别?
Ciro Santilli冠状病毒审查六四事件法轮功

区别在于您无法从模块创建实例,但是如果通用类和模块之间没有区别,则mixins并不是明确的东西,很难理解哪里是通用类,哪里是mixin
ka8725

因此在Ruby中,mixins只是无法实例化但必须用于多重继承的类?
Trilarion 2014年

6

我只是使用python mixin对python milters进行单元测试。通常情况下,军阀会与MTA对话,因此很难进行单元测试。测试混入将覆盖与MTA对话的方法,并创建由测试用例驱动的模拟环境。

因此,您采用未修改的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应用程序的测试用例中使用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


4

我认为以前的答复很好地定义了什么是MixIn。但是,为了更好地理解它们,从代码/实现的角度比较MixIn抽象类接口可能是有用的:

1.抽象类

  • 需要包含一个或多个抽象方法

  • 抽象类 可以包含状态(实例变量)和非抽象方法

2.界面

  • 接口包含抽象方法(没有非抽象方法,没有内部状态)

3. MixIns

  • 混音(如接口)包含内部状态(实例变量)
  • 混音包含一个或多个非抽象方法(与接口不同,它们可以包含非抽象方法)

在例如Python中,这些只是约定,因为以上所有内容均定义为classes。但是,抽象类,接口MixIns的共同特征是它们不应独立存在,即不应实例化。


By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.