it-swarm.com.de

Sind Python Mixins ein Anti-Pattern?

Ich bin mir völlig bewusst, dass pylint und andere statische Analysewerkzeuge nicht allwissend sind und manchmal ihre Ratschläge missachtet werden müssen. (Dies gilt für verschiedene Klassen von Nachrichten, nicht nur für conventions.)


Wenn ich Klassen wie habe

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 mit "Mixins" bezeichnet.

Wie andere Tools bewertet pylint diesen Code mit -21.67 / 10, In erster Linie, weil more_methods Und related_methods Keine self oder Attribute haben otherfunc, stack, annd my_var, da ohne Ausführen des Codes anscheinend nicht gesehen werden kann, dass related_methods und more_methods gemischt sind zu implement_methods.

Compiler und statische Analysewerkzeuge können das Halting-Problem nicht immer lösen , aber ich bin der Meinung, dass dies sicherlich ein Fall ist, in dem ein Blick auf das, was von implement_methods Geerbt wurde, zeigt, dass dies vollkommen gültig ist, und das wäre eine sehr einfache Sache.

Warum lehnen statische Analysewerkzeuge dieses gültige (glaube ich) OOP Muster) ab?

Entweder:

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

  2. mixins werden in idiomatischem, lesbarem Python nicht empfohlen


# 1 ist offensichtlich falsch, denn wenn ich pylint auffordere, mir von einer Klasse von mir zu erzählen, die unittest.TestCase Erbt und self.assertEqual Verwendet (etwas, das nur in unittest.TestCase), es beschwert sich nicht .

Sind Mixins unpythonisch oder entmutigt?

39
cat

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 in einem bestimmten Fall angemessen verwendet werden, ist eine andere Frage. Das Mixin-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 Gottklasse zu verstecken. Wenn Sie sich keinen Grund vorstellen können im Moment, eines der Mixins auszutauschen oder wegzulassen, sollte es kein Mixin sein.

18
Karl Bielefeldt

Ich glaube, dass Mixins absolut pythonisch sein können. Die idiomatische Möglichkeit, Ihren Linter zum Schweigen zu bringen - und die Lesbarkeit Ihrer Mixins zu verbessern - besteht darin, sowohl (1) abstrakte Methoden zu definieren, die explizit die Methoden definieren, die die Kinder eines Mixins implementieren müssen, als auch zu (2) vordefinierte None -wertige 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()
17
UltraBird

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

Eine gute Klasse, einschließlich Mixins, hat eine klare Verantwortung. Idealerweise sollte ein Mixin den gesamten Status enthalten, auf den es zugreifen wird, und die Logik, um damit umzugehen. Z.B. Ein gutes Mixin könnte ein last_updated Feld für eine ORM-Modellklasse, bieten Logik zum Festlegen und Methoden zum Suchen nach dem ältesten/neuesten Datensatz.

Das Verweisen auf nicht deklarierte Instanzmitglieder (Variablen und Methoden) sieht etwas seltsam aus.

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

Es kann sich um eine Mischung handeln, in der der relevante Status enthalten 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 dem Dekorateur einige Argumente übergeben müssen, um die Methodengenerierung zu beeinflussen.

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

9
9000

Dem Linter ist nicht bekannt, dass Sie eine Klasse als Mixin verwenden. Pylint ist sich 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_mixinslinter2_with_mixin

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

8
dotoscat

Sind Mixins in Ordnung?

Sind Python Mixins ein Anti-Pattern?

Mixins lassen sich nicht entmutigen - sie sind ein guter Anwendungsfall für die Mehrfachvererbung.

Warum beschwert sich Ihr Linter?

Pylint beschwert sich offensichtlich, weil er nicht weiß, wo die otherfunc, stack und my_var kommt von.

Trivial?

Es gibt keinen sofort erkennbaren guten Grund, diese beiden Methoden in separate übergeordnete Klassen zu unterteilen, entweder in Ihrem Beispiel in der Frage oder in Ihrem trivialeren verknüpften Beispiel, das hier gezeigt wird.

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, dass Ihr Linter Rauschen meldet. Dieses Rauschen kann wichtigere Probleme mit Ihrem Code verschleiern. Dies ist ein wichtiger Kostenfaktor. Ein weiterer Kostenfaktor besteht darin, dass Sie Ihren verwandten Code in verschiedene Namespaces unterteilen, 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 Wert für Sie geschaffen, der wahrscheinlich die möglichen anderen Kosten überwiegt. Wenn Sie keine Code-Wiederverwendung/Deduplizierung von Codezeilen erhalten, erhalten Sie wahrscheinlich nicht viel Wert für Ihre Mixins, und an diesem Punkt sind die Kosten für das Rauschen meiner Meinung nach höher als die Vorteile.

1
Aaron Hall