1371 Stimmen

Was ist ein Mixin und warum ist es nützlich?

En Programmierung von Python erwähnt Mark Lutz den Begriff mixin . Ich komme aus einem C/C++/C#-Hintergrund und habe den Begriff noch nie gehört. Was ist ein Mixin?

Lesen zwischen den Zeilen von dieses Beispiel (die ich verlinkt habe, weil sie recht lang ist), nehme ich an, dass es sich um einen Fall von Mehrfachvererbung handelt, um eine Klasse zu erweitern, im Gegensatz zur richtigen Unterklassifizierung. Ist das richtig?

Warum sollte ich das tun, anstatt die neue Funktionalität in eine Unterklasse aufzunehmen? Und warum wäre ein Mixin-/Mehrfachvererbungsansatz besser als die Verwendung von Komposition?

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

995voto

Jason Baker Punkte 180981

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

  1. Sie möchten viele optionale Funktionen für eine Klasse anbieten.
  2. Sie möchten ein bestimmtes Merkmal in vielen verschiedenen Klassen verwenden.

Ein Beispiel für Nummer eins ist das Anfrage- und Antwortsystem von werkzeug . Ich kann ein einfaches altes Anfrageobjekt 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

from werkzeug import BaseRequest, AcceptMixin

class Request(AcceptMixin, BaseRequest):
    pass

Wenn ich ein Anforderungsobjekt erstellen wollte, das Accept Headers, Etags, Authentifizierung und User Agent Support unterstützt, könnte ich dies tun:

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

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

Der Unterschied ist gering, aber in den obigen Beispielen wurden die Mixin-Klassen nicht für sich allein stehend erstellt. Bei der traditionelleren Mehrfachvererbung ist die AuthenticationMixin (zum Beispiel) wäre wahrscheinlich eher so etwas wie Authenticator . Das heißt, die Klasse würde wahrscheinlich so konzipiert sein, dass sie für sich alleine steht.

301voto

Randolpho Punkte 54011

Zunächst sollten Sie beachten, dass Mixins nur in Sprachen mit Mehrfachvererbung existieren. Sie können keine Mixins in Java oder C# erstellen.

Im Grunde ist ein Mixin ein eigenständiger Basistyp, der einer untergeordneten Klasse eine begrenzte Funktionalität und polymorphe Resonanz bietet. Wenn Sie in C# denken, stellen Sie sich eine Schnittstelle vor, die Sie nicht implementieren müssen, weil sie bereits implementiert ist; Sie erben einfach von ihr und profitieren von ihrer Funktionalität.

Mixins haben in der Regel einen engen Anwendungsbereich und sind nicht für Erweiterungen gedacht.

[Bearbeiten - aus welchem Grund:]

Da Sie danach gefragt haben, sollte ich wohl auf die Gründe eingehen. Der große Vorteil ist, dass Sie es nicht immer wieder selbst tun müssen. In C# könnte ein Mixin am meisten von der Entsorgungsmuster . Wenn Sie IDisposable implementieren, möchten Sie fast immer demselben Muster folgen, aber am Ende schreiben Sie denselben grundlegenden Code mit geringfügigen Variationen immer wieder neu. Wenn es ein erweiterbares Disposal-Mixin gäbe, könnte man sich eine Menge zusätzlicher Tipparbeit sparen.

[edit 2 -- um Ihre anderen Fragen zu beantworten]

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

Ja. Der Unterschied zwischen einem Mixin und der Standard-Mehrfachvererbung ist nur eine Frage der Semantik; eine Klasse mit Mehrfachvererbung kann ein Mixin als Teil dieser Mehrfachvererbung verwenden.

Der Sinn eines Mixins ist es, einen Typ zu schaffen, der durch Vererbung in einen anderen Typ "hineingemischt" werden kann, ohne den vererbenden Typ zu beeinträchtigen, während er weiterhin einige nützliche Funktionen für diesen Typ bietet.

Denken Sie wiederum an eine Schnittstelle, die bereits implementiert ist.

Ich persönlich verwende keine Mixins, da ich hauptsächlich in einer Sprache entwickle, die sie nicht unterstützt. Daher fällt es mir schwer, ein anständiges Beispiel zu finden, das Ihnen den "Aha"-Moment liefert. Aber ich werde es noch einmal versuchen. Ich werde ein Beispiel verwenden, das konstruiert ist - die meisten Sprachen bieten diese Funktion bereits auf die eine oder andere Weise an - aber das hoffentlich erklären wird, wie Mixins erstellt und verwendet werden sollen. So geht's:

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 einen String zurückgibt, der ein XML-Fragment mit den Datenwerten des Typs enthält, und eine "FromXML"-Methode, die es dem Typ ermöglicht, seine Datenwerte aus einem XML-Fragment in einem String zu rekonstruieren. Auch hier handelt es sich um ein erfundenes Beispiel. Vielleicht verwenden Sie einen Dateistrom 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ückbekommen wollen.

Der andere wichtige Punkt in diesem Beispiel ist, dass Sie dies auf eine generische Weise tun wollen. Sie wollen nicht für jeden Typ, den Sie serialisieren wollen, eine "ToXML"- und "FromXML"-Methode implementieren müssen, sondern Sie wollen ein generisches Mittel, das sicherstellt, dass Ihr Typ dies tut und es einfach funktioniert. Sie möchten Code wiederverwenden.

Wenn Ihre Sprache dies unterstützt, können Sie das Mixin XmlSerializable erstellen, das die Arbeit für Sie erledigt. Dieser Typ würde die Methoden ToXML und FromXML implementieren. Über einen Mechanismus, der für das Beispiel nicht von Bedeutung ist, wäre er in der Lage, alle erforderlichen Daten von jedem Typ zu sammeln, mit dem er gemischt ist, um das von ToXML zurückgegebene XML-Fragment zu erstellen, und er wäre ebenso in der Lage, diese Daten wiederherzustellen, wenn FromXML aufgerufen wird.

Und das war's. Um es zu verwenden, müssten Sie jeden Typ, der nach XML serialisiert werden muss, von XmlSerializable erben lassen. Wann immer Sie diesen Typ serialisieren oder deserialisieren müssen, rufen Sie einfach ToXML oder FromXML auf. Da XmlSerializable ein vollwertiger und polymorpher Typ ist, könnte man sogar einen Dokumentenserialisierer erstellen, der nichts über den ursprünglichen Typ weiß und nur, sagen wir, ein Array von XmlSerializable-Typen akzeptiert.

Stellen Sie sich nun vor, dieses Szenario für andere Dinge zu verwenden, wie z. B. die Erstellung eines Mixins, das sicherstellt, dass jede Klasse, die es einmischt, jeden Methodenaufruf protokolliert, oder ein Mixin, das dem Typ, der es einmischt, Transaktionsfähigkeit bietet. Die Liste ließe sich beliebig fortsetzen.

Wenn Sie sich ein Mixin als einen kleinen Basistyp vorstellen, der dazu dient, einem Typ eine kleine Menge an Funktionalität hinzuzufügen, ohne diesen Typ anderweitig zu beeinflussen, dann sind Sie auf der sicheren Seite.

Hoffentlich. :)

269voto

Diese Antwort zielt darauf ab, Mixins zu erklären mit Beispielen die sind:

  • Eigenständig Kurz und bündig: Sie müssen keine Bibliotheken kennen, 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 eine Python Thema.

Sie wird sich auch mit der umstrittenen Frage befassen:

Ist 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 2 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, welche davon richtig ist.

Der Konsens kann in den verschiedenen Sprachen unterschiedlich sein.

Definition 1: keine Mehrfachvererbung

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

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

Eine Einschränkung, die einige Quellen hinzufügen, ist, dass die Klasse keine Daten, sondern nur Methoden enthalten darf, aber ich sehe 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 von nur <= y == :

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 spezielle Beispiel könnte durch die functools.total_ordering() Dekorateur, 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 die von eine weitere Basisklasse und nicht durch die Ableitung wie in Definition 1.

Der Begriff Mixin-Klasse bezieht sich auf Basisklassen, die für die Verwendung in diesem Entwurfsmuster vorgesehen sind (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 in der abgeleiteten Klasse implementiert sein, in diesem Fall sind wir wieder bei Definition 1. Man muss die Intentionen des Autors berücksichtigen.

Dieses Muster ist interessant, weil es möglich ist, Funktionalitäten mit verschiedenen Basisklassen 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

Maßgebliche Python-Vorkommen

En el Offizielles Dokument für Sammlungen.abc In der Dokumentation wird ausdrücklich der Begriff Mixin-Methoden .

Sie besagt, dass wenn eine Klasse:

  • implementiert __next__
  • erbt von einer einzigen Klasse Iterator

dann erhält die Klasse eine __iter__ Mixin-Methode umsonst.

Also zumindest zu diesem Punkt der Dokumentation, mixin erfordert keine Mehrfachvererbung und ist kohärent mit Definition 1.

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

Auf dieser Seite wird auch der Begriff Set mixin was eindeutig darauf hindeutet, dass Klassen wie Set y Iterator können Mixin-Klassen genannt werden.

In anderen Sprachen

  • Rubin: Offensichtlich ist keine Mehrfachvererbung für Mixin erforderlich, wie in den wichtigsten Referenzbüchern wie Ruby programmieren und die Programmiersprache Ruby

  • C++: A virtual Methode, die eingestellt ist =0 ist eine rein virtuelle Methode.

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

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

50voto

Hamish Downer Punkte 15913

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

Nach meinem Verständnis der Konventionen, die etwas regeln, das man ein Mixin nennen würde, ist ein Mixin:

  • fügt Methoden, aber keine Instanzvariablen hinzu (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 Programmablauf nachzuvollziehen, da man nicht so viel suchen muss (im Vergleich zur vollständigen Mehrfachvererbung). Sie sind vergleichbar mit Ruby-Module .

Wenn ich Instanzvariablen hinzufügen möchte (mit mehr Flexibilität als bei der Einzelvererbung), dann neige ich dazu, die Komposition zu wählen.

Allerdings habe ich auch schon Klassen mit dem Namen XYZMixin gesehen, die Instanzvariablen haben.

46voto

Tomasz Bartkowiak Punkte 8157

Ich denke, die bisherigen Antworten haben sehr gut beschrieben, was MixIns sind. Aber, um sie besser zu verstehen, könnte es nützlich sein, zu vergleichen MixIns con Abstrakte Klassen y Schnittstellen aus der Perspektive von Code und Implementierung:

1. Abstrakte Klasse

  • Klasse die eine oder mehrere abstrakte Methoden enthalten muss

  • Abstrakte Klasse kann einen Zustand (Instanzvariablen) und nicht-abstrakte Methoden enthalten

2. Schnittstelle

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

3. MixIns

  • MixIns (wie Schnittstellen) nicht interne Zustände enthalten (Instanzvariablen)
  • MixIns eine oder mehrere nicht-abstrakte Methoden enthalten (sie kann im Gegensatz zu Schnittstellen nicht-abstrakte Methoden enthalten)

In Python z.B. sind dies nur Konventionen, denn alle oben genannten Begriffe sind definiert als class es. Das gemeinsame Merkmal der beiden Abstrakte Klassen, Interfaces y MixIns ist, dass sie no debe selbständig existieren, d.h. nicht instanziiert werden sollten.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X