什么是混合,为什么它们有用?
在“Programming Python”中,Mark Lutz提到了“mixins”。 我来自C / C ++ / C#背景,我以前没有听说过这个术语。 什么是混合?
阅读这个例子(我已经链接到,因为它很长)的行之间,我假设这是一个使用多继承来扩展一个类,而不是'正确'的子类。 这是正确的吗?
为什么我想要这样做,而不是将新功能放入子类中? 就此而言,为什么mixin / multiple inheritance方法比使用composition更好?
混合与多重继承分开了什么? 这只是一个语义问题吗?
mixin是一种特殊的多重继承。 混合使用有两种主要情况:
举一个例子,考虑werkzeug的请求和响应系统。 我可以说一个普通的旧请求对象:
from werkzeug import BaseRequest
class Request(BaseRequest):
pass
如果我想添加接受标题支持,我会做到这一点
from werkzeug import BaseRequest, AcceptMixin
class Request(BaseRequest, AcceptMixin):
pass
如果我想创建一个支持accept头,etags,验证和用户代理支持的请求对象,我可以这样做:
from werkzeug import BaseRequest, AcceptMixin, ETagRequestMixin, UserAgentMixin, AuthenticationMixin
class Request(BaseRequest, AcceptMixin, ETagRequestMixin, UserAgentMixin, AuthenticationMixin):
pass
这个差别很微妙,但在上面的例子中,mixin类并不是独立存在的。 在更传统的多重继承中, AuthenticationMixin
(例如)可能更像Authenticator
。 也就是说,这个班可能会被设计为独立。
首先,你应该注意到mixin只存在于多继承语言中。 你不能在Java或C#中进行混音。
基本上,mixin是一种独立的基本类型,为儿童课程提供有限的功能和多态共鸣。 如果您在C#中思考,请考虑一个您不必实际实现的接口,因为它已经实现; 您只需继承它并从其功能中受益。
Mixins的范围通常较窄,并不意味着延长。
[编辑 - 至于为什么:]
我想我应该解释为什么,因为你问过。 最大的好处是你不必一次又一次地自己做。 在C#中,mixin最大的好处可能来自于Disposal模式。 每当你实现IDisposable时,你几乎总是希望遵循相同的模式,但是最终你会写出并重写相同的基本代码,并做出很小的变化。 如果有一个可扩展的Disposal mixin,你可以节省很多额外的输入。
[编辑2 - 回答你的其他问题]
混合与多重继承分开了什么? 这只是一个语义问题吗?
是。 mixin和标准多重继承之间的区别只是语义问题; 一个具有多重继承的类可能会使用一个mixin作为该多重继承的一部分。
mixin的意义在于创建一种可以通过继承“混合”到任何其他类型的类型,而不会影响继承类型,同时仍然为该类型提供一些有益的功能。
再次考虑一个已经实现的接口。
我个人不使用mixins,因为我主要使用不支持它们的语言进行开发,所以我很难想出一个体面的例子来提供这个“ahah!”。 你的时刻。 但我会再试一次。 我将使用一个人为设计的例子 - 大多数语言已经以某种方式提供了这个功能,但希望能够解释mixin是如何被创建和使用的。 开始:
假设你有一种你想能够序列化到XML的类型。 您希望该类型提供一个“ToXML”方法,该方法返回一个包含具有该类型数据值的XML片段的字符串,以及一个允许类型从字符串中的XML片段重建其数据值的“FromXML”。 再次,这是一个人为的例子,所以也许你使用文件流,或者从你的语言的运行库中的XML编写器类......不管。 关键是你想要将对象序列化为XML并从XML中获取新对象。
在这个例子中的另一个重点是你想以通用的方式做到这一点。 您不希望为每个想要序列化的类型实现“ToXML”和“FromXML”方法,您需要一些通用的方法来确保您的类型可以执行此操作并且它可以正常工作。 你想要重复使用代码。
如果您的语言支持它,您可以创建XmlSerializable mixin为您完成工作。 这种类型将实现ToXML和FromXML方法。 它会使用一些对该示例不重要的机制来收集来自任何类型的所有必要数据,以便构建由ToXML返回的XML片段,并且在FromXML为调用。
而..就是这样。 要使用它,你需要将任何需要序列化成XML的类型从XmlSerializable继承。 无论何时需要序列化或反序列化该类型,都可以简单地调用ToXML或FromXML。 事实上,由于XmlSerializable是一个完全成熟的类型和多态,你可以设想建立一个文档序列化器,它不知道任何关于你的原始类型,只接受一个XmlSerializable类型的数组。
现在想象一下使用这个场景来做其他事情,比如创建一个mixin,确保每个混合它的类都记录每个方法调用,或者为混合它的类型提供事务性的混合。列表可以继续。
如果您只是将mixin想象为一种小型基础类型,旨在将少量功能添加到类型中,而不会影响该类型,那么您就是金牌。
希望。 :)
这个答案旨在解释mixins 的例子 :
独立 :简而言之,不需要了解任何图书馆就能理解这个例子。
在Python中 ,不在其他语言中。
可以理解的是,有其他语言例如Ruby的例子,因为这个术语在这些语言中更常见,但这是一个Python线程。
它还应考虑有争议的问题:
是否需要多重继承来表征mixin?
定义
我还没有看到来自“权威”来源的引用,清楚地说明了什么是Python中的mixin。
我已经看到了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()
装饰器来实现,但这里的游戏是重新发明轮子:
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使用该方法的那些或实现它的那些?)
要确定一个给定的类是否是混合的并不容易:该方法可以在派生类上实现,在这种情况下,我们回到定义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发生
在文件collection.abc的官方文档中,文档明确使用了术语Mixin Methods。
它指出如果一个类:
__next__
Iterator
继承 那么这个类可以免费获得__iter__
mixin方法。
因此,至少在文档的这一点上, mixin不需要多重继承 ,并且与定义1相一致。
这些文档当然可能在不同的地方有矛盾,其他重要的Python库可能会在其文档中使用其他定义。
该页面还使用术语Set mixin
,它清楚地表明像Set
和Iterator
这样的类可以称为Mixin类。
在其他语言中
Ruby:正如主要参考书籍(如Programming Ruby和Ruby编程语言)中提到的,显然不需要对mixin进行多重继承
C ++:未实现的方法是纯虚拟方法。
定义1与抽象类(具有纯虚拟方法的类)的定义相一致。 该类不能被实例化。
定义2可能具有虚拟继承:来自两个派生类的多重继承