Was ist ein Mixin und warum sind sie nützlich?

954

In " Programming Python " erwähnt Mark Lutz "Mixins". Ich komme aus C / C ++ / C # und habe den Begriff noch nicht gehört. Was ist ein Mixin?

Wenn ich zwischen den Zeilen dieses Beispiels lese (auf das ich verlinkt habe, weil es ziemlich lang ist), gehe ich davon aus, dass es sich um die Verwendung der Mehrfachvererbung handelt, um eine Klasse zu erweitern, im Gegensatz zur 'richtigen' Unterklasse. Ist das richtig?

Warum sollte ich das tun wollen, anstatt die neue Funktionalität in eine Unterklasse zu stellen? Warum sollte ein Mixin / Multiple-Vererbungs-Ansatz besser sein als die Verwendung von Komposition?

Was unterscheidet ein Mixin von Mehrfachvererbung? Ist es nur eine Frage der Semantik?

TarkaDaal
quelle

Antworten:

710

Ein Mixin ist eine besondere Art der Mehrfachvererbung. Es gibt zwei Hauptsituationen, in denen Mixins verwendet werden:

  1. Sie möchten viele optionale Funktionen für eine Klasse bereitstellen.
  2. Sie möchten eine bestimmte Funktion in vielen verschiedenen Klassen verwenden.

Betrachten Sie als Beispiel für Nummer eins das Anforderungs- und Antwortsystem von werkzeug . Ich kann ein einfaches altes Anforderungsobjekt erstellen, indem ich sage:

from werkzeug import BaseRequest

class Request(BaseRequest):
    pass

Wenn ich die Unterstützung für Accept-Header hinzufügen möchte, würde ich das machen

from werkzeug import BaseRequest, AcceptMixin

class Request(AcceptMixin, BaseRequest):
    pass

Wenn ich ein Anforderungsobjekt erstellen möchte, das das Akzeptieren von Headern, Etags, Authentifizierung und Benutzeragenten unterstützt, kann ich Folgendes tun:

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

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

Der Unterschied ist subtil, aber in den obigen Beispielen wurden die Mixin-Klassen nicht für sich allein gestellt. Bei einer traditionelleren Mehrfachvererbung AuthenticationMixinwäre das (zum Beispiel) wahrscheinlich eher so Authenticator. Das heißt, die Klasse wäre wahrscheinlich so konzipiert, dass sie für sich allein steht.

Jason Baker
quelle
123
Eine dritte Situation ist: Sie möchten viele (nicht optionale) Funktionen für eine Klasse bereitstellen, aber Sie möchten die Funktionen in separaten Klassen (und in separaten Modulen), sodass jedes Modul ungefähr eine Funktion (Verhalten) IOW umfasst, nicht zur Wiederverwendung, aber zur Unterteilung.
Bootchk
60
Wahrscheinlich kein Problem in diesem Beispiel, aber Sie möchten im Allgemeinen die Hauptbasisklasse als letztes Element in die Klammer setzen, um die Vererbungskette zu erstellen: Request ==> Mixin ==> ... ==> BaseRequest. Siehe hier: ianlewis.org/en/mixins-and-python
hillel
10
@hillel guter Punkt, aber denken Sie daran, dass Python die Methoden von Superklassen von links nach rechts aufruft (wenn Sie beispielsweise den Konstruktor überschreiben müssen).
Eliseu Monar dos Santos
9
Das klingt sehr nach dem Decorator-Designmuster.
D-Jones
4
Eine vierte Situation ist: Es gibt bereits eine bestehende Familie der ParentKlasse und Child1, Child2, ChildNUnterklassen innerhalb einer 3rd - Party - Bibliothek, und Sie wollen ein individuelles Verhalten für die ganze Familie. Idealerweise möchten Sie ein solches Verhalten hinzufügen Parentund hoffen, dass der Bibliotheksentwickler eines Drittanbieters Ihre Pull-Anfrage entgegennimmt. Andernfalls müssen Sie Ihre eigenen implementieren class NewBehaviorMixinund dann einen vollständigen Satz von Wrapper-Klassen wie class NewParent(NewBehaviorMixin, Parent): passund class NewChildN(NewBehaviorMixin, ChildN): passusw. definieren. (PS: Kennen Sie einen besseren Weg?)
RayLuo
240

Zunächst sollten Sie beachten, dass Mixins nur in Sprachen mit Mehrfachvererbung vorhanden sind. Sie können in Java oder C # kein Mixin erstellen.

Grundsätzlich ist ein Mixin ein eigenständiger Basistyp, der eine eingeschränkte Funktionalität und polymorphe Resonanz für eine Kinderklasse bietet. Wenn Sie in C # denken, denken Sie an eine Schnittstelle, die Sie nicht implementieren müssen, da sie bereits implementiert ist. Sie erben einfach davon und profitieren von seiner Funktionalität.

Mixins haben normalerweise einen engen Umfang und sind nicht dazu gedacht, erweitert zu werden.

[bearbeiten - warum:]

Ich nehme an, ich sollte mich mit dem Warum befassen, da Sie gefragt haben. Der große Vorteil ist, dass Sie es nicht immer wieder selbst tun müssen. In C # könnte der größte Ort, an dem ein Mixin profitieren könnte, das Entsorgungsmuster sein . Wann immer Sie IDisposable implementieren, möchten Sie fast immer dem gleichen Muster folgen, aber am Ende schreiben und schreiben Sie den gleichen Basiscode mit geringfügigen Abweichungen neu. Wenn es ein erweiterbares Entsorgungsmixin gäbe, könnten Sie sich viel zusätzliches Tippen sparen.

[Bearbeiten 2 - um Ihre anderen Fragen zu beantworten]

Was unterscheidet ein Mixin von Mehrfachvererbung? Ist es nur eine Frage der Semantik?

Ja. Der Unterschied zwischen einem Mixin und einer Standard-Mehrfachvererbung ist nur eine Frage der Semantik. Eine Klasse mit Mehrfachvererbung verwendet möglicherweise ein Mixin als Teil dieser Mehrfachvererbung.

Der Sinn eines Mixins besteht darin, einen Typ zu erstellen, der über die Vererbung in jeden anderen Typ "eingemischt" werden kann, ohne den ererbenden Typ zu beeinflussen, und dennoch einige nützliche Funktionen für diesen Typ bietet.

Stellen Sie sich erneut eine Schnittstelle vor, die bereits implementiert ist.

Ich persönlich verwende keine Mixins, da ich mich hauptsächlich in einer Sprache entwickle, die sie nicht unterstützt. Daher fällt es mir wirklich schwer, ein anständiges Beispiel zu finden, das nur das "ahah!" Moment für dich. Aber ich werde es noch einmal versuchen. Ich werde ein Beispiel verwenden, das erfunden wurde - die meisten Sprachen bieten die Funktion bereits auf die eine oder andere Weise an -, aber das wird hoffentlich erklären, wie Mixins erstellt und verwendet werden sollen. Hier geht:

Angenommen, Sie haben einen Typ, den Sie in und aus XML serialisieren möchten. Sie möchten, dass der Typ eine "ToXML" -Methode bereitstellt, die eine Zeichenfolge zurückgibt, die ein XML-Fragment mit den Datenwerten des Typs enthält, und eine "FromXML" -Methode, mit der der Typ seine Datenwerte aus einem XML-Fragment in einer Zeichenfolge rekonstruieren kann. Auch dies ist ein erfundenes Beispiel. Vielleicht verwenden Sie einen Dateistream oder eine XML Writer-Klasse aus der Laufzeitbibliothek Ihrer Sprache ... was auch immer. Der Punkt ist, dass Sie Ihr Objekt in XML serialisieren und ein neues Objekt aus XML zurückerhalten möchten.

Der andere wichtige Punkt in diesem Beispiel ist, dass Sie dies generisch tun möchten. Sie möchten nicht für jeden Typ, den Sie serialisieren möchten, eine "ToXML" - und eine "FromXML" -Methode implementieren müssen. Sie möchten generische Mittel, um sicherzustellen, dass Ihr Typ dies tut und es einfach funktioniert. Sie möchten die Wiederverwendung von Code.

Wenn Ihre Sprache dies unterstützt, können Sie das XmlSerializable-Mixin erstellen, um Ihre Arbeit für Sie zu erledigen. Dieser Typ würde die Methoden ToXML und FromXML implementieren. Mithilfe eines Mechanismus, der für das Beispiel nicht wichtig ist, kann es alle erforderlichen Daten von jedem Typ erfassen, mit dem es gemischt ist, um das von ToXML zurückgegebene XML-Fragment zu erstellen, und es kann diese Daten auch wiederherstellen, wenn FromXML vorhanden ist namens.

Und das ist es. Um es zu verwenden, müssten Sie jeden Typ, der in XML serialisiert werden muss, von XmlSerializable erben. Wann immer Sie diesen Typ serialisieren oder deserialisieren mussten, riefen Sie einfach ToXML oder FromXML auf. Da XmlSerializable ein vollwertiger und polymorpher Typ ist, können Sie möglicherweise einen Dokumentenserialisierer erstellen, der nichts über Ihren ursprünglichen Typ weiß und beispielsweise nur ein Array von XmlSerializable-Typen akzeptiert.

Stellen Sie sich nun vor, Sie verwenden dieses Szenario für andere Zwecke, z. B. zum Erstellen eines Mixins, das sicherstellt, dass jede Klasse, die es einmischt, jeden Methodenaufruf protokolliert, oder als Mixin, das dem Typ, der es einmischt, Transaktionsfähigkeit verleiht. Die Liste kann weiter und weiter gehen.

Wenn Sie sich ein Mixin nur als einen kleinen Basistyp vorstellen, der einem Typ eine kleine Menge an Funktionalität hinzufügen soll, ohne diesen Typ anderweitig zu beeinflussen, dann sind Sie golden.

Hoffnungsvoll. :) :)

Randolpho
quelle
25
Hey, magst du diesen Ausdruck "polymorphe Resonanz"? Ich habe es mir ausgedacht. Ich glaube. Vielleicht habe ich es irgendwo in der Physik gehört ...
Randolpho
50
Ich bin mit Ihrem ersten Satz nicht einverstanden. Ruby ist eine Einzelvererbungssprache und Mixins sind die Möglichkeit, einer bestimmten Klasse Methoden hinzuzufügen, ohne von einer anderen Klasse geerbt zu werden.
Keltia
23
@ Keltia: Ich denke, dass Mixins per Definition Mehrfachvererbung sind. Im Ruby-Fall handelt es sich um ein Monkeypatch (oder etwas anderes), das kein richtiges Mixin ist. Die Ruby-Leute nennen es vielleicht ein Mixin, aber es ist etwas anderes.
S.Lott
10
Tatsächlich kann ein echtes Mixin keine Mehrfachvererbung verwenden. Ein Mixin enthält Methoden, Attribute usw. von einer Klasse in einer anderen, ohne diese zu erben. Dies führt dazu, dass die Vorteile der Wiederverwendung von Code mit Polymorphismus erscheinen, lässt jedoch die Probleme bei der Bestimmung der Abstammung (der Diamant des Todes usw.) aus. Mixin-unterstützende Sprachen ermöglichen auch die teilweise Einbeziehung der Mixin-Klasse (die Dinge beginnen sich ein bisschen so anzuhören Aspekte jetzt).
Trevor
8
Für den Datensatz unterstützt Java jetzt Mixins mit Standardmethoden.
Shmosel
170

Diese Antwort zielt darauf ab, Mixins anhand von Beispielen zu erklären :

  • in sich geschlossen : kurz, ohne dass Sie Bibliotheken kennen müssen, um das Beispiel zu verstehen.

  • in Python , nicht in anderen Sprachen.

    Es ist verständlich, dass es Beispiele aus anderen Sprachen wie Ruby gab, da der Begriff in diesen Sprachen viel häufiger vorkommt, aber dies ist ein Python- Thread.

Es wird auch die kontroverse Frage berücksichtigt:

Ist eine Mehrfachvererbung notwendig oder nicht, um ein Mixin zu charakterisieren?

Definitionen

Ich habe noch kein Zitat aus einer "maßgeblichen" Quelle gesehen, das klar sagt, was ein Mixin in Python ist.

Ich habe zwei mögliche Definitionen eines Mixins gesehen (wenn sie sich von anderen ähnlichen Konzepten wie abstrakten Basisklassen unterscheiden sollen), und die Leute sind sich nicht ganz einig, welches richtig ist.

Der Konsens kann zwischen verschiedenen Sprachen variieren.

Definition 1: keine Mehrfachvererbung

Ein Mixin ist eine Klasse, bei der eine Methode der Klasse eine Methode verwendet, die in der Klasse nicht definiert ist.

Daher soll die Klasse nicht instanziiert werden, sondern als Basisklasse dienen. Andernfalls verfügt die Instanz über Methoden, die nicht aufgerufen werden können, ohne eine Ausnahme auszulösen.

Eine Einschränkung, die einige Quellen hinzufügen, ist, dass die Klasse möglicherweise keine Daten enthält, sondern nur Methoden, aber ich verstehe nicht, warum dies notwendig ist. In der Praxis haben jedoch viele nützliche Mixins keine Daten, und Basisklassen ohne Daten sind einfacher zu verwenden.

Ein klassisches Beispiel ist die Implementierung aller Vergleichsoperatoren nur von <=und ==:

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 

Dieses besondere Beispiel hätte über den functools.total_ordering()Dekorateur erreicht werden können, aber hier ging es darum, das Rad neu zu erfinden:

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)

Definition 2: Mehrfachvererbung

Ein Mixin ist ein Entwurfsmuster, bei dem eine Methode einer Basisklasse eine Methode verwendet, die sie nicht definiert, und von der diese Methode implementiert werden soll anderen Basisklasse , nicht von der in Definition 1 abgeleiteten.

Der Begriff Mixin-Klasse bezieht sich auf Basisklassen, die in diesem Entwurfsmuster verwendet werden sollen (TODO diejenigen, die die Methode verwenden, oder diejenigen, die sie implementieren?).

Es ist nicht einfach zu entscheiden, ob eine bestimmte Klasse ein Mixin ist oder nicht: Die Methode könnte nur für die abgeleitete Klasse implementiert werden. In diesem Fall kehren wir zu Definition 1 zurück. Sie müssen die Absichten des Autors berücksichtigen.

Dieses Muster ist interessant, weil es möglich ist, Funktionalitäten mit verschiedenen Auswahlmöglichkeiten von Basisklassen neu zu kombinieren:

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

Autorisierende Python-Vorkommen

In der offiziellen Dokumentation für Sammlungen.abc wird in der Dokumentation ausdrücklich der Begriff Mixin-Methoden verwendet .

Es heißt, wenn eine Klasse:

  • Geräte __next__
  • erbt von einer einzelnen Klasse Iterator

dann bekommt die Klasse eine __iter__ Mixin-Methode kostenlose .

Zumindest in diesem Punkt der Dokumentation erfordert Mixin daher keine Mehrfachvererbung und mit Definition 1 überein.

Die Dokumentation kann natürlich an verschiedenen Stellen widersprüchlich sein, und andere wichtige Python-Bibliotheken verwenden möglicherweise die andere Definition in ihrer Dokumentation.

Auf dieser Seite wird auch der Begriff verwendet Set mixin, der eindeutig darauf hinweist, dass Klassen Mixin-Klassen mögen Setund als Iteratorsolche bezeichnet werden können.

In anderen Sprachen

  • Ruby: Es ist klar, dass für das Mixin keine Mehrfachvererbung erforderlich ist, wie in wichtigen Nachschlagewerken wie Programming Ruby und The Ruby Programming Language erwähnt

  • C ++: Eine nicht implementierte Methode ist eine rein virtuelle Methode.

    Definition 1 stimmt mit der Definition einer abstrakten Klasse überein (eine Klasse mit einer rein virtuellen Methode). Diese Klasse kann nicht instanziiert werden.

    Definition 2 ist mit virtueller Vererbung möglich: Mehrfachvererbung von zwei abgeleiteten Klassen

Ciro Santilli 冠状 病毒 审查 六四 事件 法轮功
quelle
37

Ich betrachte sie als eine disziplinierte Methode zur Verwendung der Mehrfachvererbung - denn letztendlich ist ein Mixin nur eine weitere Python-Klasse, die (möglicherweise) den Konventionen über Klassen folgt, die Mixins genannt werden.

Mein Verständnis der Konventionen, die etwas regeln, das Sie als Mixin bezeichnen würden, ist das eines Mixins:

  • fügt Methoden hinzu, aber keine Instanzvariablen (Klassenkonstanten sind OK)
  • erbt nur von object(in Python)

Auf diese Weise wird die potenzielle Komplexität der Mehrfachvererbung begrenzt, und es ist relativ einfach, den Ablauf Ihres Programms zu verfolgen, indem Sie einschränken, wo Sie suchen müssen (im Vergleich zur vollständigen Mehrfachvererbung). Sie ähneln Ruby-Modulen .

Wenn ich Instanzvariablen hinzufügen möchte (mit mehr Flexibilität als bei der Einzelvererbung), tendiere ich zur Komposition.

Trotzdem habe ich Klassen namens XYZMixin gesehen, die Instanzvariablen haben.

Hamish Downer
quelle
30

Mixins ist ein Konzept in der Programmierung, bei dem die Klasse Funktionen bereitstellt, jedoch nicht zur Instanziierung verwendet werden soll. Der Hauptzweck von Mixins besteht darin, eigenständige Funktionen bereitzustellen. Es ist am besten, wenn die Mixins selbst keine Vererbung mit anderen Mixins aufweisen und auch den Zustand vermeiden. In Sprachen wie Ruby gibt es eine direkte Sprachunterstützung, für Python jedoch keine. Sie können jedoch die Vererbung mehrerer Klassen verwenden, um die in Python bereitgestellten Funktionen auszuführen.

Ich habe dieses Video gesehen http://www.youtube.com/watch?v=v_uKI2NOLEM , um die Grundlagen von Mixins zu verstehen. Für Anfänger ist es sehr nützlich, die Grundlagen von Mixins und deren Funktionsweise sowie die Probleme bei der Implementierung zu verstehen.

Wikipedia ist immer noch das Beste: http://en.wikipedia.org/wiki/Mixin

Lakesh
quelle
29

Was unterscheidet 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.

Insbesondere im Kontext von Python ist ein Mixin eine übergeordnete Klasse, die Unterklassen Funktionen bietet, jedoch nicht selbst instanziiert werden soll.

Was Sie dazu veranlassen könnte zu sagen, "das ist nur Mehrfachvererbung, nicht wirklich ein Mixin", ist, wenn die Klasse, die für ein 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),)

Es werden sowohl das Counterals auch das OrderedDictvom collectionsModul untergeordnet.

Beide Counterund OrderedDictsollen instanziiert und alleine verwendet werden. Indem wir beide unterordnen, können wir jedoch einen Zähler haben, der geordnet ist und den Code in jedem Objekt wiederverwendet.

Dies ist eine leistungsstarke Methode zur Wiederverwendung von Code, kann jedoch auch problematisch sein. Wenn sich herausstellt, dass eines der Objekte einen Fehler enthält, kann durch sorgfältiges Beheben ein Fehler in der Unterklasse entstehen.

Beispiel eines Mixins

Mixins werden normalerweise als Weg zur Wiederverwendung von Code ohne potenzielle Kopplungsprobleme beworben, die eine kooperative Mehrfachvererbung wie der OrderedCounter haben könnte. Wenn Sie Mixins verwenden, verwenden Sie Funktionen, die nicht so eng mit den Daten verbunden sind.

Im Gegensatz zum obigen Beispiel ist ein Mixin nicht für die alleinige Verwendung vorgesehen. Es bietet neue oder andere Funktionen.

Beispielsweise enthält die Standardbibliothek einige Mixins in der socketserverBibliothek .

Mit diesen Mix-In-Klassen können Forking- und Threading-Versionen für jeden Servertyp erstellt werden. Beispielsweise wird ThreadingUDPServer wie folgt erstellt:

class ThreadingUDPServer(ThreadingMixIn, UDPServer):
    pass

Die Mix-In-Klasse steht an erster Stelle, da sie eine in UDPServer definierte Methode überschreibt. Durch das Festlegen der verschiedenen Attribute wird auch das Verhalten des zugrunde liegenden Servermechanismus geändert.

In diesem Fall überschreiben die Mixin-Methoden die Methoden in der UDPServerObjektdefinition, um Parallelität zu ermöglichen.

Die überschriebene Methode scheint zu sein process_requestund bietet auch eine andere Methode process_request_thread. Hier ist es aus dem 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 den Nutzen dieses Repräsentanten 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 Verwendung wäre:

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)
Aaron Hall
quelle
11

Ich denke, hier gab es einige gute Erklärungen, aber ich wollte eine andere Perspektive bieten.

In Scala können Sie Mixins ausführen, wie hier beschrieben wurde. Sehr interessant ist jedoch, dass die Mixins tatsächlich zusammengeführt werden, um eine neue Art von Klasse zu erstellen, von der geerbt werden soll. Im Wesentlichen erben Sie nicht von mehreren Klassen / Mixins, sondern generieren eine neue Art von Klasse mit allen Eigenschaften des Mixins, von dem geerbt werden soll. Dies ist sinnvoll, da Scala auf der JVM basiert, bei der Mehrfachvererbung derzeit nicht unterstützt wird (ab Java 8). Dieser Mixin-Klassentyp ist übrigens ein spezieller Typ, der in Scala als Merkmal bezeichnet wird.

Es wird in der Art und Weise angedeutet, wie eine Klasse definiert wird: Klasse NewClass erweitert FirstMixin mit SecondMixin mit ThirdMixin ...

Ich bin mir nicht sicher, ob der CPython-Interpreter dasselbe tut (Mixin-Class-Komposition), aber ich wäre nicht überrascht. Aus einem C ++ - Hintergrund stammend, würde ich auch kein ABC oder "Interface" nennen, das einem Mixin entspricht - es ist ein ähnliches Konzept, aber unterschiedlich in Verwendung und Implementierung.

SilentDirge
quelle
9

Ich würde von Mix-Ins in neuem Python-Code abraten, wenn Sie einen anderen Weg finden (z. B. Komposition statt Vererbung oder nur Methoden zum Patchen von Affen in Ihre eigenen Klassen), der nicht viel mehr ist Anstrengung.

In Klassen alten Stils können Sie Mix-Ins verwenden, um einige Methoden aus einer anderen Klasse abzurufen. Aber in der Welt des neuen Stils erbt alles, sogar das Mix-In object. Das bedeutet, dass jede Verwendung der Mehrfachvererbung natürlich zu MRO-Problemen führt .

Es gibt Möglichkeiten, MRO mit Mehrfachvererbung in Python zum Laufen zu bringen, insbesondere die Funktion super (). Dies bedeutet jedoch, dass Sie Ihre gesamte Klassenhierarchie mit super () ausführen müssen, und es ist erheblich schwieriger, den Kontrollfluss zu verstehen.

Bobince
quelle
3
Seit Version 2.3 verwendet Python die "C3-Methodenauflösung", die in der Python 2.3-Methodenauflösungsreihenfolge oder Methodenauflösungsreihenfolge erläutert wird .
Webwurst
11
Persönlich würde ich in den meisten Fällen Mixins über das Patchen von Affen nehmen; Es ist einfacher, über den Code nachzudenken und ihn zu befolgen.
tdammers
5
Abgestimmt. Während Ihre Antwort eine gültige Meinung zu Entwicklungsstilen ausdrückt, sprechen Sie die eigentliche Frage nicht wirklich an.
Ryan B. Lynch
8

Vielleicht helfen ein paar Beispiele.

Wenn Sie eine Klasse erstellen und möchten, dass sie sich wie ein Wörterbuch verhält, können Sie alle __ __erforderlichen Methoden definieren . Aber das ist ein bisschen schmerzhaft. Alternativ können Sie nur einige definieren und (zusätzlich zu jeder anderen Vererbung) von UserDict.DictMixin(verschoben nach ) erbencollections.DictMixin in py3k verschoben) erben. Dies hat zur Folge, dass automatisch der gesamte Rest der Wörterbuch-API definiert wird.

Ein zweites Beispiel: Mit dem GUI-Toolkit wxPython können Sie Listensteuerelemente mit mehreren Spalten erstellen (z. B. die Dateianzeige im Windows Explorer). Standardmäßig sind diese Listen ziemlich einfach. Sie können zusätzliche Funktionen hinzufügen, z. B. die Möglichkeit, die Liste nach einer bestimmten Spalte zu sortieren, indem Sie auf die Spaltenüberschrift klicken, von ListCtrl erben und entsprechende Mixins hinzufügen.

John Fouhy
quelle
8

Es ist kein Python-Beispiel, aber in der Programmiersprache Dmixin bezieht sich der Begriff auf ein Konstrukt, das auf die gleiche Weise verwendet wird. einer Klasse einen Haufen Zeug hinzufügen.

In D (was übrigens kein MI macht) wird dies durch Einfügen einer Vorlage (denken Sie an syntaktisch bewusste und sichere Makros und Sie werden nah dran sein) in einen Bereich gemacht. Dies ermöglicht es einer einzelnen Codezeile in einer Klasse, Struktur, Funktion, einem Modul oder was auch immer, auf eine beliebige Anzahl von Deklarationen erweitert zu werden.

BCS
quelle
2
Mixin ist ein allgemeiner Begriff, der in D, Ruby usw. verwendet wird. Laut Wikipedia stammten sie aus Lisp-Systemen der alten Schule und wurden erstmals 1983 dokumentiert: en.wikipedia.org/wiki/…
Lee B
7

OP erwähnte, dass er / sie noch nie von Mixin in C ++ gehört hat, vielleicht weil sie in C ++ als CRTP (Curiously Recurring Template Pattern) bezeichnet werden. Außerdem erwähnte @Ciro Santilli, dass Mixin über eine abstrakte Basisklasse in C ++ implementiert wird. Während abstrakte Basisklassen zum Implementieren von Mixin verwendet werden können, ist dies ein Overkill, da die Funktionalität der virtuellen Funktion zur Laufzeit mithilfe der Vorlage zur Kompilierungszeit erreicht werden kann, ohne dass der Aufwand für die Suche nach virtuellen Tabellen zur Laufzeit anfällt.

Das CRTP-Muster wird hier ausführlich beschrieben

Ich habe das Python-Beispiel in der Antwort von @Ciro Santilli mithilfe der folgenden Vorlagenklasse in C ++ konvertiert:

    #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;
}

BEARBEITEN: In ComparableMixin wurde ein geschützter Konstruktor hinzugefügt, sodass er nur vererbt und nicht instanziiert werden kann. Das Beispiel wurde aktualisiert, um zu zeigen, wie ein geschützter Konstruktor beim Erstellen eines Objekts von ComparableMixin einen Kompilierungsfehler verursacht.

bigdata2
quelle
Mixins und CRTP sind in C ++ nicht genau dasselbe.
Ashrasmun
6

Vielleicht kann ein Beispiel von Ruby helfen:

Sie können das Mixin einschließen Comparableund eine Funktion definieren "<=>(other)". Das Mixin bietet alle diese Funktionen:

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

Dies geschieht durch Aufrufen <=>(other)und Zurückgeben des richtigen Ergebnisses.

"instance <=> other"Gibt 0 zurück, wenn beide Objekte gleich sind, weniger als 0, wenn sie instancegrößer als sind, otherund mehr als 0, wenn sie othergrößer sind.

Georg Schölly
quelle
Hier ist ein Beitrag, der ein ähnliches Mixin für Python bereitstellt. Obwohl der Vorschlag __lt__stattdessen als Basis definiert wird __cmp__, wird letzterer tatsächlich veraltet und von der Verwendung abgeraten. Mir scheint es einfacher , dass mixin zu verwenden , anstatt ziemlich kompliziert Dekorateure (Teil functools ) - obwohl dies eine Lage sein kann , mehr dynamisch zu reagieren auf denen Vergleiche sind ...
Tobias Kienzler
6

Mit mixin können Sie einer Klasse Funktionen hinzufügen, dh Sie können mit in einem Modul definierten Methoden interagieren, indem Sie das Modul in die gewünschte Klasse aufnehmen. Ruby unterstützt zwar keine Mehrfachvererbung, bietet aber als Alternative Mixin an, um dies zu erreichen.

Hier ist ein Beispiel, das erklärt, wie Mehrfachvererbung mit Mixin erreicht wird.

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
Akash Soti
quelle
4
Was ist der Unterschied zwischen dieser und der Mehrfachvererbung im Allgemeinen?
Ciro Santilli 法轮功 病毒 审查 六四 事件 13
Der Unterschied besteht darin, dass Sie keine Instanzen aus Modulen erstellen können. Wenn jedoch keine Unterscheidung zwischen allgemeinen Klassen und Modulen erfolgt, sind die Mixins nicht explizit und es ist schwer zu verstehen, wo sich eine allgemeine Klasse befindet und wo sich ein Mixin befindet
ka8725
In Ruby sind Mixins also nur Klassen, die nicht instanziiert werden können, sondern für die Mehrfachvererbung verwendet werden müssen?
Trilarion
6

Ich habe gerade ein Python-Mixin verwendet, um Unit-Tests für Python-Milters durchzuführen. Normalerweise spricht ein Milter mit einem MTA, was das Testen von Einheiten schwierig macht. Das Testmixin überschreibt Methoden, die mit dem MTA kommunizieren, und erstellt stattdessen eine simulierte Umgebung, die von Testfällen gesteuert wird.

Sie nehmen also eine unveränderte Milter-Anwendung wie spfmilter und mischen TestBase wie folgt:

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)

Verwenden Sie dann TestMilter in den Testfällen für die Milter-Anwendung:

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='[email protected]')
  self.assertEqual(rc,Milter.CONTINUE)
  milter.close()

http://pymilter.cvs.sourceforge.net/viewvc/pymilter/pymilter/Milter/test.py?revision=1.6&view=markup

Stuart Gathman
quelle
4

Ich denke, frühere Antworten haben sehr gut definiert, was MixIns sind. Um sie besser zu verstehen, kann es jedoch nützlich sein, MixIns mit abstrakten Klassen und Schnittstellen aus der Code- / Implementierungsperspektive zu vergleichen :

1. Abstrakte Klasse

  • Klasse , die eine oder mehrere abstrakte Methoden enthalten muss

  • Die abstrakte Klasse kann Status- (Instanzvariablen) und nicht abstrakte Methoden enthalten

2. Schnittstelle

  • Schnittstelle enthält abstrakte Methoden nur (keine nicht-abstrakte Methoden und kein interner Zustand)

3. MixIns

  • MixIns (wie Interfaces) enthalten keinen internen Status (Instanzvariablen)
  • MixIns enthalten eine oder mehrere nicht abstrakte Methoden (sie können im Gegensatz zu Schnittstellen nicht abstrakte Methoden enthalten).

In zB Python sind dies nur Konventionen, da alle oben genannten als classes definiert sind . Das gemeinsame Merkmal von Abstract Classes, Interfaces und MixIns ist jedoch, dass sie nicht alleine existieren sollten, dh nicht instanziiert werden sollten.

Tomasz Bartkowiak
quelle
3

Ich habe gelesen, dass Sie einen AC # Hintergrund haben. Ein guter Ausgangspunkt könnte also eine Mixin-Implementierung für .NET sein.

Vielleicht möchten Sie das Codeplex-Projekt unter http://remix.codeplex.com/ überprüfen.

Sehen Sie sich den Link zum lang.net-Symposium an, um sich einen Überblick zu verschaffen. Die Dokumentation auf der Codeplex-Seite enthält noch weitere Informationen.

Grüße Stefan

Stefan Papp
quelle