Was trennt ein Mixin von Mehrfachvererbung? Ist es nur eine Frage der Semantik?
Ein Mixin ist eine begrenzte Form der Mehrfachvererbung. In einigen Sprachen unterscheidet sich der Mechanismus zum Hinzufügen eines Mixins zu einer Klasse geringfügig (in Bezug auf die Syntax) von dem der Vererbung.
Im Kontext von Python ist ein Mixin eine übergeordnete Klasse, die Funktionen für Unterklassen bereitstellt, aber selbst nicht instanziiert werden soll.
Was Sie dazu veranlassen könnte zu sagen, "das ist nur Mehrfachvererbung, nicht wirklich ein Mixin", ist, wenn die Klasse, die mit einem Mixin verwechselt werden könnte, tatsächlich instanziiert und verwendet werden kann - es ist also tatsächlich ein semantischer und sehr realer Unterschied.
Beispiel für Mehrfachvererbung
Dieses Beispiel, aus der Dokumentation ist ein 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),)
Sie ist eine Unterklasse sowohl der Counter
und die OrderedDict
del collections
Modul.
Les deux Counter
y OrderedDict
sind dazu bestimmt, instanziiert und eigenständig verwendet zu werden. Indem wir sie jedoch beide unterklassifizieren, können wir einen Zähler haben, der geordnet ist und den Code in jedem Objekt wiederverwendet.
Dies ist eine gute Möglichkeit, Code wiederzuverwenden, kann aber auch problematisch sein. Wenn sich herausstellt, dass eines der Objekte einen Fehler enthält, kann die unvorsichtige Behebung dieses Fehlers einen Fehler in der Unterklasse verursachen.
Beispiel für ein Mixin
Mixins werden in der Regel als Mittel zur Wiederverwendung von Code ohne potenzielle Kopplungsprobleme beworben, die bei kooperativer Mehrfachvererbung, wie dem OrderedCounter, auftreten können. Wenn Sie Mixins verwenden, verwenden Sie Funktionen, die nicht so eng an die Daten gekoppelt sind.
Anders als im obigen Beispiel ist ein Mixin nicht dazu gedacht, allein verwendet zu werden. Es bietet neue oder andere Funktionen.
Die Standardbibliothek hat zum Beispiel eine Reihe von Mixins in der socketserver
Bibliothek .
Von jedem Servertyp können Forking- und Threading-Versionen erstellt werden mithilfe dieser Mix-In-Klassen erstellt werden. Zum Beispiel wird ThreadingUDPServer wie folgt erstellt:
class ThreadingUDPServer(ThreadingMixIn, UDPServer):
pass
Die Mix-in-Klasse steht an erster Stelle, da sie über UDPServer definiert ist. Das Setzen der verschiedenen Attribute ändert auch das Verhalten der des zugrunde liegenden Servermechanismus.
In diesem Fall überschreiben die Mixin-Methoden die Methoden in der UDPServer
Objektdefinition, um Gleichzeitigkeit zu ermöglichen.
Die überschriebene Methode scheint zu sein process_request
und bietet auch eine andere Methode, process_request_thread
. Hier ist es von der Quellcode :
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()
Ein erfundenes Beispiel
Dies ist ein Mixin, das hauptsächlich zu Demonstrationszwecken dient - die meisten Objekte werden sich über die Nützlichkeit dieser Repräsentation hinaus entwickeln:
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)
)
und Nutzung sein:
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__()
Und Verwendung:
>>> f1 = Foo('bar')
>>> f2 = Foo()
>>> f1
Foo(foo='bar')
>>> f2
Foo(foo=None)