混入是一种特殊的多重继承方式,主要有两种情况需要使用混入:
例如第一种情况可以考虑 werkzeug 的请求和响应系统。 我可以通过以下方式创建一个普通的请求对象:
from werkzeug import BaseRequest
class Request(BaseRequest):
pass
如果我想添加接受头支持,我会这样做
from werkzeug import BaseRequest, AcceptMixin
class Request(AcceptMixin, BaseRequest):
pass
如果我想创建一个支持接受头、Etags、身份认证和用户代理支持的请求对象,我可以这样做:
from werkzeug import BaseRequest, AcceptMixin, ETagRequestMixin, UserAgentMixin, AuthenticationMixin
class Request(AcceptMixin, ETagRequestMixin, UserAgentMixin, AuthenticationMixin, BaseRequest):
pass
这两种方式的区别微妙,但在上面的例子中,mixin类并不是为了独立存在而设计的。在更传统的多重继承中,AuthenticationMixin
(例如)可能更像Authenticator
。也就是说,该类可能被设计成可以独立存在。
首先,你应该注意到mixin只存在于支持多继承的语言中。你不能在Java或C#中使用mixin。
基本上,mixin是一个独立的基础类型,为子类提供有限的功能和多态谐振。如果你想用C#来思考,可以把它看作一个接口,因为它已经被实现了,所以你只需继承它并从它的功能中受益。
Mixin通常范围狭窄,不意味着要扩展。
[编辑-关于为什么:]
既然你问了,我觉得我应该解释一下为什么。最大的好处是你不必反复自己写同样的代码。在C#中,mixin可能最有用的地方可能是清理模式。每当你实现IDisposable时,你几乎总是需要遵循相同的模式,但你最终会编写和重写具有微小差异的相同基本代码。如果有可扩展的Disposal mixin,你可以节省很多额外的打字。
[编辑2 - 回答你的其他问题]
一个mixin和多重继承的区别是什么?只是语义上的区别吗?
是的。mixin和标准多重继承之间的区别只是语义上的;具有多重继承的类可能会在其多重继承中使用mixin。
mixin的目的是创建一个类型,可以通过继承“混入”到任何其他类型中,而不影响继承类型,同时为该类型提供一些有益的功能。
再次思考已经实现的接口。
我个人不使用mixin,因为我主要在不支持它们的语言中进行开发,所以我很难想出一个合适的例子来让你豁然开朗。但我会再试试。我将使用一个虚构的例子--大多数语言已经以某种方式提供了这个特性--但希望能够解释如何创建和使用mixin。试试看:
假设你有一个需要进行XML序列化和反序列化的类型。你想要这个类型提供一个"ToXML"方法,返回一个包含该类型数据值的XML片段字符串,以及一个"FromXML"方法,允许该类型从XML片段字符串中重构其数据值。这只是一个假设的例子,因此可能使用文件流,或来自语言运行时库的XML Writer类...无论如何,关键是你想将对象序列化为XML,并从XML获得一个新对象。定义 我还没有看到 "权威" 来源的引用清楚地说明 Python 中的 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 出现
在 集合.abc 的官方文档 中,文件明确使用术语 Mixin 方法。
如果一个类:
__next__
Iterator
继承那么该类将免费获得一个 __iter__
mixin 方法。
因此,至少在这一点上,mixin 不需要多重继承,并且与定义 1 一致。
当然,文档在不同的地方可能会相互矛盾,并且其他重要的 Python 库可能在它们的文档中使用其他定义。
本页面还使用术语 Set mixin
,这清楚地表明类如 Set
和 Iterator
可以被称为 Mixin 类。
在其他语言中
Ruby:如主要参考书籍Programming Ruby和《Ruby编程语言》所述,明显不需要多重继承进行混合。
C++:设置=0
的virtual
方法是纯虚方法。
定义1与抽象类的定义相同(具有纯虚方法的类),该类无法实例化。
使用虚拟继承可能会出现定义2:Multiple Inheritance from two derived classes
Set
和Iterator
这样的类可以被称为Mixin类。那么它是否与您的Mixin类定义1相矛盾,该定义要求Mixin类使用它们未定义的方法,因为Iterator
违反了该要求(参见其实现)? - Géry Ogam我认为它们是使用多重继承的一种有纪律的方式 - 因为 mixin 最终只是另一个 Python 类,遵循了所谓的混合类约定。
我的理解是,所谓 Mixin 的约定如下:
object
(在 Python 中)继承这样它就限制了多重继承的潜在复杂性,并通过限制查看范围来使程序流程跟踪相对容易(与完整的多重继承相比)。 它们类似于Ruby 模块。
如果我想要添加实例变量(比单继承允许的更灵活),那么我倾向于使用组合。
话虽如此,我确实见过被称为 XYZMixin 的类具有实例变量的情况。
我认为之前的回答已经很好地定义了什么是MixIns,但是为了更好地理解它们,与从代码/实现角度比较MixIns和Abstract Classes以及Interfaces可能会有所帮助:
1. 抽象类
需要包含一个或多个抽象方法的类
抽象类可以包含状态(实例变量)和非抽象方法
2. 接口
接口仅包含抽象方法(没有非抽象方法和内部状态)
3. MixIns
MixIns(像接口一样)不包含内部状态(实例变量)
MixIns包含一个或多个非抽象方法(与接口不同,它们可以包含非抽象方法)
在Python等语言中,这只是约定,因为以上所有内容都被定义为class
。然而,抽象类、接口和MixIns的共同特点是它们不应单独存在,即不应被实例化。
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),)
它从collections
模块继承了Counter
和OrderedDict
两个类。
Counter
和OrderedDict
都是可以单独实例化和使用的。然而,通过同时继承它们,我们可以得到一个有序的计数器,并且可以重复使用每个对象中的代码。
这是一种强大的代码重用方式,但也可能会有问题。如果发现其中一个对象存在漏洞,在不小心修复的情况下可能会在子类中产生错误。
混入通常被视为在没有潜在耦合问题的情况下获得代码重用的方法,例如OrderedCounter
中的协作多重继承。当您使用混入时,您使用的功能与数据之间的耦合程度不那么紧密。
与上面的示例不同,混入不打算单独使用。它提供新的或不同的功能。
例如,标准库中有一些socketserver
库中的混入。
在这种情况下,mixin方法覆盖了Forking and threading versions of each type of server can be created using these mix-in classes. For instance, ThreadingUDPServer is created as follows:
class ThreadingUDPServer(ThreadingMixIn, UDPServer): pass
The mix-in class comes first, since it overrides a method defined in UDPServer. Setting the various attributes also changes the behavior of the underlying server mechanism.
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()
这是一个主要用于演示目的的混合项 - 大多数对象将超出此 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)
混入(Mixins)是编程中的一个概念,类提供了功能,但不适合用于实例化。混入的主要目的是提供独立的功能,最好在混入本身与其他混入没有继承关系并避免状态。在像Ruby这样的语言中,有一些直接支持的语言,但是在Python中没有。但是,可以使用多类继承来执行Python中提供的功能。
我观看了这个视频http://www.youtube.com/watch?v=v_uKI2NOLEM,以了解混入的基础知识。对于初学者来说,它非常有用,可以了解混入的基础知识、工作原理以及在实现混入时可能面临的问题。
维基百科仍然是最好的:http://en.wikipedia.org/wiki/Mixin
我认为这里已经有一些很好的解释了,但我想提供另一种观点。
在Scala中,可以像这里所述那样进行mixin,但非常有趣的是,这些mixin实际上被“融合”在一起,以创建一种新的类继承。实质上,您不会从多个类/ mixin继承,而是生成一种具有所有mixin属性以继承的新类型的类。这是有道理的,因为Scala基于JVM,在Java 8时还不支持多重继承。顺便说一下,这种mixin类类型是Scala中称为特征(Trait)的一种特殊类型。
这是通过定义类的方式进行暗示的: class NewClass extends FirstMixin with SecondMixin with ThirdMixin ...
我不确定CPython解释器是否执行相同的操作(mixin类组合),但我并不感到惊讶。此外,来自C++背景的我不会将ABC或“接口”等同于mixin-它们是类似的概念,但在用途和实现上有所不同。
混入类名通常以“-MixIn”、“-able”或“-ible”结尾,以强调它们的性质,就像Python标准库中socketserver
模块的ThreadingMixIn
和ForkingMixIn
类以及collections.abc
模块的Hashable
、Iterable
、Callable
、Awaitable
、AsyncIterable
和Reversible
类一样。
这里是一个抽象混入内置类Sized
的示例,用于单继承中通过扩展子类Queue
和Stack
的接口,提供__len__
特殊方法:
import abc
import collections.abc
class Queue(collections.abc.Sized, metaclass=abc.ABCMeta):
@abc.abstractmethod
def enqueue(self, item):
raise NotImplementedError
@abc.abstractmethod
def dequeue(self):
raise NotImplementedError
class Stack(collections.abc.Sized, metaclass=abc.ABCMeta):
@abc.abstractmethod
def push(self, item):
raise NotImplementedError
@abc.abstractmethod
def pop(self):
raise NotImplementedError
LoggingMixIn
,用于多重继承,通过扩展内置super类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'
或许一些例子可以帮助理解。
如果你正在构建一个类,想让它像一个字典一样工作,你可以定义所有必要的 __ __
方法。但这有点麻烦。作为替代方案,你可以只定义一部分方法,并从 UserDict.DictMixin
(在py3k中移动到collections.DictMixin
)继承(除了其他继承)。这将自动定义出其余的字典API。
第二个例子:GUI工具包wxPython允许你创建具有多列的列表控件(例如Windows资源管理器中的文件显示)。默认情况下,这些列表比较基础。你可以通过从ListCtrl继承并添加适当的mixins来增加额外的功能,例如按列标题点击对列表进行排序的功能。