mixins - 什么是mixin , 他们为什么有用?

  显示原文与译文双语对照的内容

在"编程 python"中,标记Lutz提到"mixin"。 我来自 C/C++/C# 背景,我还没有听说过这个术语。 什么是 mixin?

阅读于行间的本示例 ( 我已经链接了,因为它很长),我是作为反对'正确'presuming它是一个案例,可以使用多重继承来扩展一个类子类化。 这是对的我为什么要这么做,而不是把新的功能放到子类中? 因此,为什么 mixin/多重继承方法比使用组合更好?

将mixin与多重继承分离? 这只是语义问题?

时间:

在多个inheritance,围绕确切地描述一个混合是我很希望能够把我的头- 具体而言,什么将有掺烧比例?

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

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

对于第一个示例,考虑请求werkzeug和响应系统 。 我可以通过以下方法创建一个普通的旧请求对象:


from werkzeug import BaseRequest

class Request(BaseRequest):
 pass

如果我想添加接受头支持,我将使


from werkzeug import BaseRequest, AcceptMixin

class Request(BaseRequest, AcceptMixin):
 pass

如果我想创建一个支持接受标头,etag,身份验证和 User Agent 支持的请求对象,我可以这样做:


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

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

差异是微妙的,但是在上面的例子中,mixin类不是独立的。 在更传统的多重继承中,AuthenticationMixin ( 例如) 可能更像是验证器。 也就是说,这个类可能被设计成独立于自己的。

首先,你应该注意到mixin只存在于multiple-inheritance语言中。 你不能在Java或者 C# 中执行 mixin 。

基本上,mixin是一个独立的基类型,它为子类提供有限的功能和多态共振。 如果你在 C# 中思考,你可以考虑一个你不需要实现的接口,因为它已经实现;你只从它继承并受益于它的功能。

mixin在范围上很窄,并不意味着扩展。

[edit -- as to why: ]

我想我应该解决为什么,因为你问。 最大的好处是你不必反复地做它。 在 C# 中,mixin可以受益的最大地方可能是处理模式 。 当你实现IDisposable时,你几乎总是想要遵循相同的模式,但是最终你会编写和使用一些小的变体来编写相同的基本代码。 如果有可以扩展的处理 mixin,你可以节省很多额外的输入。

[edit yf_num_eaza8888_yf_num -- to answer your other questions ]

将mixin与多重继承分离? 这只是语义问题?

是的,mixin和标准多重继承之间的差异只是语义问题;具有多重继承的类可能使用mixin作为多重继承的一部分。

mixin的要点是创建一个类型,它可以通过继承对任何其他类型进行"混合于",而不会影响继承类型,同时仍然为该类型提供一些有益的功能。

同样,考虑一个已经实现的接口。

我个人不使用 mixin,因为我主要是用一种不支持的语言开发的,所以我现在遇到了一个很好的例子,它只是给你提供了"ahah"时刻。 但是我会再试一次。我将使用一个虚构的--大多数语言已经提供了特性,但这将以某种方式提供特性,但这将解释如何创建和使用 mixin 。 这里是:

假设你有一个类型,你希望能够将它的序列化为和从 XML 。 你希望该类型提供一个"toxml"方法,该方法返回一个包含 XML Fragment的字符串,该字符串包含类型的数据值,以及允许类型从字符串中的XML Fragment重建它的数据值的"fromXML"。 同样,这是一个虚构的例子,所以你可以使用一个文件流或者一个从你的语言库运行时的XML编写器类。 无论如何,要点是你想要将对象序列化为 XML,并从XML获取一个新对象。

本示例中的另一个要点是,你希望以通用的方式执行这里操作。 你不想为要序列化的每种类型都实现一个"toxml"和"fromXML"方法,你需要一些一般方法来确保你的类型能够做到这一点,并且它只是工作。 你想要代码重用。

如果你的语言支持它,你可以创建 XmlSerializable mixin来为你做你的工作。 这种类型将实现ToXML和FromXML方法。 它使用某种不重要的机制,能够收集任何类型的必要数据,并将它的用于构建ToXML的XML Fragment,当FromXML被调用时,它同样能够恢复数据。

然后。要使用它,要使用它,你需要将任何需要序列化到XmlSerializable的类型从序列化。 每当你需要序列化或者反序列化那种类型时,你只需调用ToXML或者 FromXML 。 实际上,由于XmlSerializable是一种fully-fledged类型和多态,你可以构建一个不了解你的原始类型的文档序列化程序,只接受一个XmlSerializable类型的数组。

现在想象一下,使用这个场景,比如创建一个 mixin,确保每个类都记录了每个方法调用,或者一个提供事务的mixin,该类型为混合它的类型提供事务性。 List 可以继续和打开。

如果你只是将mixin看作是一个很小的基类型,旨在在不影响那种类型的情况下向类型添加少量的功能,那么你就是黄金。

希望:)

举例说明 are,,这里答案旨在解释 mixin :

  • 独立的 。: short,不需要知道任何库来理解示例。

  • 在 python,而不是其他语言中。

    ,证实有例子用其它语言比如 ruby 因为这完全可以理解这个术语是在那些语言中更为常见,但这是一个 python 线程。

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

是否需要多重继承?

磅定义

我还没有看到来自"权威"源的引用,清楚地说什么是 python 中的mixin 。

我已经看到了 2个混合( 如果被认为不同于其他类似概念,比如抽象基类)的可能定义,人们并不完全认同哪一个是正确的。

不同语言之间的一致性可能不同。

定义 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_ordering() decorator实现的,但是这里的游戏是为了重新创建轮子:


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

一种混合是一个设计模式,它不定义一个基类的某个方法使用了一个方法,并在其中通过另一个基类这个方法旨在被实现,而不是由派生 1中类似定义。

术语混合类指的是基类,它的适用于再在那个设计模式( 待办事项哪些使用的方法,或者是那些实现它)?

确定给定类是否是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 事件

为 collections.abc 处的官方 documentatiton 该文档显式地使用术语 掺烧比例方法。

它声明如果一个类:

  • 实现 __next__
  • 从单个类 Iterator 继承

然后这个类获得一个 __iter__ 混合方法 。

定义因此至少在这一点上的文档,掺烧比例不不需要多重继承,并且又 coherent.

文档在不同的地方可能是矛盾的,其他重要的python 库可能在他们的文档中使用其他定义。

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

其他语言的

  • ruby: 显然不需要对mixin进行多重继承,如编程 ruby 和 ruby 编程语言等主要参考书中提到

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

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

    定义 2是不可能的,因为不能从 C++ 中的其他基类实现纯虚方法。

我可以把它们想像成按规定的方式来使用多重继承的- 因为最终套混合是另一类 python 类,该类( 可能) 按照约定关于一些类,称为 mixin 。

我对控制你所谓的Mixin的约定的理解是 Mixin:

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

这样就限制了多重继承的潜在复杂性,并通过限制你的程序的位置来相当容易地跟踪程序流。 它们类似于 ruby 模块。

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

说过,我看到的类XYZMixin确实有实例变量。

我也会建议跨多个mix-ins在新的代码,如果你能找到任何其他方式在它的周围( 比如 composition-instead-of-inheritance,或者只是monkey-patching方法到你自己的类) python,也没有太大精力。

在old-style类中,你可以使用mix-ins作为从另一个类获取一些方法的方法。 但是在new-style世界中,甚至是 mix-in,继承自 object 。 这意味着对多重继承的任何使用都会自然而然地引入 MRO问题。

有一些方法可以让 multiple-inheritance MRO在 python 中工作,最显著的是 super() 函数,但是这意味着你必须使用 super(), 来完成整个类的层次结构,这就很难理解控制流程。

ruby的一个例子可以帮助你:

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


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

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

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

也许有几个例子可以帮助你。

如果你正在构建一个类,并且希望它像字典一样操作,你可以定义所有需要的__ __ 方法。 但这有点痛苦。 在 UserDict.DictMixin 作为备选方案,你可以只定义了一些,并继承 这将会自动定义字典api的其余部分。

第二个示例:( 例如 Windows 资源管理器中的文件显示) 工具包wxPython允许你使用多个列创建列表控件。 默认情况下,这些列表是相当基本的。 在ListCtrl和施加合适的mixins,你可以添加额外的功能,如通过inheriting.能力通过一个特定的列通过单击列标题对列表进行排序,

mixin是编程中的一个概念,该类提供了功能,但它并不是用于实例化的。 mixin的主要目的是提供独立的功能,如果mixin本身没有与其他mixin的继承,并且也避免了状态。 在诸如 ruby 这样的语言中,有一些直接语言支持,但对于 python,没有。 但是,你可以使用multi-class继承来执行 python 中提供的功能。

我观看了这个视频 http://www.youtube.com/watch?v=v_uKI2NOLEM,了解了mixin的基础知识。 对于初学者来说,理解mixin的基本知识以及他们如何工作以及你在实现它们时面临的问题是非常有用的。

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

它不是一个在D 节目语言该术语 python 示例但在mixin 是用来引用一个构造基本相同的方法;对类添加一堆的东西来使用。

在( 顺便说一下,这并不意味着) 中,通过将一个模板( 思考语法和安全的宏,你就会关闭) 插入一个作用域来完成。 这允许在类,结构,函数,模块或者任何其他的声明中使用一行代码。

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

...