Sind Python-Mixins ein Anti-Pattern?

34

Mir ist völlig bewusst, dass pylintund andere statische Analysewerkzeuge nicht allwissend sind, und manchmal müssen ihre Ratschläge missachtet werden. (Dies gilt für verschiedene Nachrichtenklassen, nicht nur für conventions.)


Wenn ich Unterricht haben möchte

class related_methods():

    def a_method(self):
        self.stack.function(self.my_var)

class more_methods():

    def b_method(self):
        self.otherfunc()

class implement_methods(related_methods, more_methods):

    def __init__(self):
        self.stack  = some()
        self.my_var = other()

    def otherfunc(self):
        self.a_method()

Offensichtlich ist das erfunden. Hier ist ein besseres Beispiel, wenn Sie möchten .

Ich glaube, dieser Stil wird "Mixins" genannt.

Wie bei anderen Tools wird pylintdieser Code mit bewertet -21.67 / 10, vor allem, weil er denkt more_methodsund related_methodskeine selfoder Attribute otherfunchat stack, und my_varweil er ohne Ausführung des Codes anscheinend nicht sehen kann related_methodsund darin more_methodsgemischt ist implement_methods.

Compiler und statische Analysewerkzeuge können das Problem des Anhaltens nicht immer lösen , aber ich bin der Meinung, dass ein Blick auf das, was von geerbt implement_methodswird, durchaus berechtigt ist, und das wäre sehr einfach.

Warum lehnen statische Analysetools dieses gültige (meiner Meinung nach) OOP-Muster ab?

Entweder:

  1. Sie versuchen nicht einmal , die Vererbung zu überprüfen oder

  2. Mixins werden in idiomatischem, lesbarem Python nicht empfohlen


Nr. 1 ist offensichtlich falsch, da ich pylintmich nicht beschwere, wenn ich nach einer Klasse von mir frage , die unittest.TestCaseverwendet self.assertEqual(etwas, das nur in definiert ist unittest.TestCase) .

Sind Mixins unpythonisch oder entmutigt?

Katze
quelle
1
Ist das eine schlechte Frage? Ist das Off-Topic? ist es unbeantwortbar? Bin ich dumm? Die Leute können DV, aber sie wollen nicht dazu beitragen, es zu verbessern.
Katze
1
Es gibt Leute, die es wahrscheinlich satt haben zu fragen: "Ist das ein (Anti) Muster?" Oder "Ist das (Un) Pythonisch?".
9
@ MichaelT Das ist in Ordnung. Sie können dann aufhören, Fragen zu überprüfen.
Katana314
2
@tac-Benutzer werden aus verschiedenen Gründen abstimmen, müssen jedoch nie angeben, warum - auf der Schaltfläche "Abstimmen" befindet sich ein Tooltipp mit der Aufschrift "Diese Frage zeigt keinen Forschungsaufwand an; es ist unklar oder nicht nützlich" - vage, wenn akzeptable Antwort. 8 hoch und 1 runter ist eigentlich ziemlich gut, vor allem bei einer Frage, bei der Downvotes kostenlos sind. Lass dich nicht unterkriegen. Prost.
Aaron Hall
@AaronHall Mir ist bewusst, dass die Leute mit ihren Stimmen machen dürfen, was sie wollen, und ich sage neuen Nutzern, die sich über dasselbe beschweren.
Katze

Antworten:

16

Mixins sind einfach kein Anwendungsfall, der vom Tool berücksichtigt wurde. Das bedeutet nicht, dass es notwendigerweise ein schlechter Anwendungsfall ist, nur ein ungewöhnlicher für Python.

Ob Mixins im Einzelfall angemessen eingesetzt werden, ist eine andere Frage. Das Anti-Pattern, das ich am häufigsten sehe, verwendet Mixins, wenn immer nur eine Kombination gemischt werden soll. Das ist nur ein Umweg, um eine Gottesklasse zu verstecken. Wenn Sie nicht aus einem Grunde denken können , gerade jetzt zu tauschen oder auslassen eine der Mixins, sollte es kein mixin sein.

Karl Bielefeldt
quelle
Ja, es ist ein göttliches Objekt, gebe ich zu. In diesem Fall würde ich argumentieren, dass es in Ordnung ist, wenn die CPU ein Gott-Objekt ist.
Katze
15

Ich glaube, dass Mixins absolut pythonisch sein können. Die idiomatische Methode, um Ihren Linter zum Schweigen zu bringen und die Lesbarkeit Ihrer Mixins zu verbessern, besteht darin, (1) abstrakte Methoden zu definieren, die explizit die Methoden definieren, die die untergeordneten Elemente eines Mixins implementieren müssen, und (2) vordefinieren None-bewertete Felder für Mixin-Datenelemente, die Kinder initialisieren müssen.

Anwenden dieses Musters auf Ihr Beispiel:

from abc import ABC, abstractmethod


class related_methods():
    my_var = None
    stack = None

    def a_method(self):
        self.stack.function(self.my_var)


class more_methods(ABC):
    @abstractmethod
    def otherfunc(self):
        pass

    def b_method(self):
        self.otherfunc()


class implement_methods(related_methods, more_methods):
    def __init__(self):
        self.stack  = some()
        self.my_var = other()

    def otherfunc(self):
        self.a_method()
UltraBird
quelle
2
+1 für Ihre Antwort, obwohl die abstract other(self): passSache die Verwirrung des linter für meine tauscht
Katze
Es ist noch verwirrender ohne die @abstract-Methode ;-) Ich habe zuerst mit Java angefangen, also ist das in Ordnung für mich.
Chris Huang-Leaver
9

Ich denke, Mixins können gut sein, aber ich denke auch, dass Pylint in diesem Fall richtig ist. Haftungsausschluss: Es folgt meinungsbasiertes Material.

Eine gute Klasse, Mixins eingeschlossen, hat eine klare Verantwortung. Idealerweise sollte ein Mixin den gesamten Status enthalten, auf den es zugreifen wird, sowie die Logik, mit der es umgeht. last_updatedZum Beispiel könnte ein gutes Mixin ein Feld zu einer ORM-Modellklasse hinzufügen , eine Logik zum Festlegen und Methoden zum Suchen nach dem ältesten / neuesten Datensatz bereitstellen.

Auf nicht deklarierte Instanzmitglieder (Variablen und Methoden) zu verweisen, sieht etwas seltsam aus.

Der richtige Ansatz hängt stark von der jeweiligen Aufgabe ab.

Es kann sich um eine Mischung mit dem relevanten Status handeln, der darin gespeichert ist.

Es kann sich um eine andere Klassenhierarchie handeln, bei der sich die Methoden, die Sie derzeit über ein Mixin verteilen, in einer Basisklasse befinden, während Implementierungsunterschiede auf niedrigerer Ebene zu Unterklassen gehören. Dies scheint für Ihren Fall mit den Stapeloperationen am besten geeignet zu sein.

Es kann ein Klassendekorateur sein, der eine oder zwei Methoden hinzufügt. Dies ist normalerweise sinnvoll, wenn Sie einige Argumente an den Decorator übergeben müssen, um die Methodengenerierung zu beeinflussen.

Geben Sie Ihr Problem an, erklären Sie Ihre größeren Designprobleme, und wir könnten uns darüber streiten, ob etwas in Ihrem Fall ein Anti-Pattern ist.

9000
quelle
6

Dem Linter ist nicht bekannt, dass Sie eine Klasse als Mixin verwenden. Pylint ist bewusst, dass Sie ein Mixin verwenden, wenn Sie das Suffix 'mixin' oder 'Mixin' am Ende des Klassennamens hinzufügen, dann hört der Linter auf, sich zu beschweren.

linter_without_mixins linter2_with_mixin

Mixins sind an sich nicht schlecht oder gut, sie sind nur ein Werkzeug. Sie machen sie eine gute oder eine schlechte Verwendung.

dotoscat
quelle
2
Dies liest sich eher wie ein Kommentar, siehe Wie man antwortet
Mücke
2
Dies ist eine wertvolle Antwort, weil ich glaube, ich würde nie etwas anderes über diesen Hinweis erfahren
Katze
1

Sind Mixins in Ordnung?

Sind Python-Mixins ein Anti-Pattern?

Mixins werden nicht entmutigt - sie sind ein guter Anwendungsfall für die Mehrfachvererbung.

Warum beschwert sich dein Linter?

Pylint beschwert sich offenbar , weil es nicht , wo das nicht kennt otherfunc, stackund my_var herkommt.

Trivial?

Es gibt keinen offensichtlichen Grund, diese beiden Methoden in separate übergeordnete Klassen zu unterteilen, weder in Ihrem Beispiel in der Frage noch in Ihrem hier gezeigten trivialeren verknüpften Beispiel.

201
202 class OpCore():
203     # nothing runs without these
204
205 class OpLogik(): 
206     # logic methods... 
207 
208 class OpString(): 
209     # string things
210 
211 
212 class Stack(OpCore, OpLogik, OpString): 
213 
214     "the mixin mixer of the above mixins" 

Kosten für Mixins und Lärm

Die Kosten für das, was Sie tun, verursachen Lärm in Ihrem Linter-Bericht. Dieses Rauschen kann wichtigere Probleme mit Ihrem Code verdecken. Dies ist ein wichtiger Kostenfaktor. Ein weiterer Nachteil ist, dass Sie Ihren zusammengehörigen Code in verschiedene Namespaces aufteilen, wodurch es für Programmierer möglicherweise schwieriger wird, die Bedeutung von zu erkennen.

Fazit

Die Vererbung ermöglicht die Wiederverwendung von Code. Wenn Sie Code mit Ihren Mixins wiederverwenden, haben sie einen Mehrwert für Sie geschaffen, der wahrscheinlich die möglichen anderen Kosten überwiegt. Wenn Sie keine Wiederverwendung / Deduplizierung von Codezeilen erhalten, erhalten Sie wahrscheinlich nicht viel Wert für Ihre Mixins, und zu diesem Zeitpunkt sind die Kosten für das Rauschen meiner Meinung nach höher als die Vorteile.

Aaron Hall
quelle
Es gibt einen guten Grund, sie in dem verknüpften, nicht trivialen Beispiel zu trennen - es ist einfacher für mich, meinen Code zu organisieren, wenn ich einen Bruchteil einer vollständigen Klasse habe, die Mathematik implementiert, eine andere, die Zeichenfolgen usw. implementiert, und Sie mischen sie zusammen Forth Suppe zu bekommen.
Katze
"Ein weiterer Nachteil ist, dass Sie Ihren Code in verschiedene Namespaces unterteilen, was es für Programmierer möglicherweise schwieriger macht, die Bedeutung von" - nein, das ist überhaupt nicht der Fall. Die Klassennamensräume machen es einfacher zu sagen, was eine Gruppe von Methoden tut, und wenn jemand den Stack verwenden möchte, sollte er die Forth Soup aufrufen / implementieren, nicht die einzelnen Zutaten
cat
@cat wahrscheinlich, weil sie erfunden und nicht in einem wirklich großen Projekt verwendet werden. Kein Beispiel unterstützt wirklich Ihre Argumente für die Verwendung von Mixins. Beide zeigen nur eine Verwendung des Mixins. In diesem Fall ist es einfacher, die Implementierung an einem Ort zu platzieren, um Stil und Organisation zu verbessern. Wenn Sie nun einen Fall zeigen könnten, in dem Sie gemeinsame Funktionen in verschiedenen Klassen, aber keine gemeinsame Basisklasse möchten, würden Sie dort ein Mixin verwenden. Welches ist neben Ihrer fusseligen Frage (UltraBird hat das gut beantwortet). Dies beantwortet jedoch allgemein Ihre Frage zur Anwendbarkeit des Mixins.
Dlamblin