Les mixins Python sont-ils un anti-motif?

34

Je suis tout à fait conscient que d' pylintautres outils d'analyse statique ne savent pas tout et qu'il faut parfois désobéir à leurs conseils. (Ceci s'applique à différentes classes de messages, pas seulement à l' conventionart.)


Si j'ai des cours comme

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()

Évidemment, c'est artificiel. Voici un meilleur exemple, si vous aimez .

Je crois que ce style s'appelle "mixins".

Comme d’autres outils, attribue une note à pylintce code -21.67 / 10, principalement parce qu’il pense more_methodset related_methodsn’a pas d’ selfattributs otherfunc, mais parce que sans exécuter le code, il ne peut apparemment pas voir et est mélangé à .stackmy_varrelated_methodsmore_methodsimplement_methods

Les compilateurs et les outils d’analyse statique ne peuvent pas toujours résoudre le problème Halting , mais j’estime que c’est un cas dans lequel regarder ce qui en a été hérité implement_methodsmontrerait que cela est parfaitement valide et que ce serait très facile à faire.

Pourquoi les outils d’analyse statique rejettent-ils ce modèle de POO valide (je pense)?

Non plus:

  1. Ils ne sont même pas essayer de vérifier l' héritage ou

  2. les mixins sont déconseillés en Python lisible et idiomatique


Le numéro 1 est évidemment incorrect, car si je demande pylintà me parler d’une classe à moi qui hérite unittest.TestCasequi utilise self.assertEqual, (quelque chose défini uniquement dans unittest.TestCase), elle ne se plaint pas .

Les mixins sont-ils non rythmiques ou découragés?

chat
la source
1
Est-ce une mauvaise question? Est-ce hors sujet? est-ce impossible? Suis-je stupide? Les gens peuvent DV mais ils ne veulent pas aider à l'améliorer.
chat
1
Il y en a qui sont fatigués de se faire demander "est-ce un modèle (anti)) ou" est-ce (un) pythonique ".
9
@MichaelT C'est bien. Ils peuvent alors arrêter de passer en revue les questions.
Katana314
2
Les gens de @tac voteront vers le bas pour diverses raisons, mais ne seront jamais obligés de dire pourquoi - le bouton de vote par défaut indique une info-bulle disant: "Cette question ne montre aucun effort de recherche; elle n'est ni claire ni utile" - un flou, si réponse acceptable. 8 haut et 1 bas est en fait assez bon, en particulier sur une question, où les votes négatifs sont gratuits. Ne laissez pas ça vous abattre. À votre santé.
Aaron Hall
@AaronHall Je suis conscient que les gens sont autorisés à faire ce qu'ils veulent avec leurs votes. En effet, je dis aux nouveaux utilisateurs qui se plaignent de la même chose.
Chat

Réponses:

16

Les mixins ne sont tout simplement pas un cas d'utilisation pris en compte par l'outil. Cela ne veut pas dire que c'est forcément un mauvais cas d'utilisation, juste un cas rare pour python.

Que les mixins soient utilisés de manière appropriée dans un cas particulier est un autre problème. L'anti-pattern mixin que je vois le plus fréquemment consiste à utiliser mixins lorsqu'il n'y a jamais eu l'intention de mélanger une combinaison. C'est juste une façon détournée de cacher une classe de dieu. Si vous ne pouvez pas penser à une raison en ce moment d'échanger sur ou laisser de côté l' un des mixins, il ne devrait pas être un mixin.

Karl Bielefeldt
la source
oui, c'est un objet divin, je l'avoue. Dans ce cas, je dirais qu'il est normal que le processeur soit un objet divin.
chat
15

Je crois que les Mixins peuvent être, bien sûr, Pythonic. Cependant, le moyen le plus idiomatique de réduire au silence votre linter - et d’améliorer la lisibilité de vos Mixins - consiste à la fois (1) à définir des méthodes abstraites définissant explicitement les méthodes que les enfants d’un Mixin doivent mettre en œuvre et (2) à prédéfinir Nonechamps évalués pour les membres de données Mixin que les enfants doivent initialiser.

Appliquer ce modèle à votre exemple:

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
la source
2
+1 pour votre réponse, bien que la abstract other(self): passchose échange la confusion du linter pour le mien
cat
C'est encore plus déroutant sans la méthode @abstractmethod ;-). J'ai d'abord commencé avec Java, donc c'est très bien pour moi.
Chris Huang-Leaver le
9

Je pense que les mixins peuvent être bons, mais je pense aussi que pylint a raison dans ce cas. Clause de non-responsabilité: des éléments d'opinion suivent.

Une bonne classe, mixins compris, a une responsabilité claire. Idéalement, un mixin devrait contenir tout l'état auquel il va accéder et la logique pour le gérer. Par exemple, un bon mélange peut ajouter un last_updatedchamp à une classe de modèle ORM, fournir une logique pour le définir et des méthodes pour rechercher l'enregistrement le plus ancien / le plus récent.

Faire référence à des membres d'instance non déclarés (variables et méthodes) semble un peu bizarre.

La bonne approche dépend beaucoup de la tâche à accomplir.

Il peut s'agir d'un mélange avec le bit d'état retenu.

Il peut s'agir d'une hiérarchie de classes différente dans laquelle les méthodes que vous distribuez actuellement via un mixin appartiennent à une classe de base, tandis que les différences d'implémentation de niveau inférieur appartiennent à des sous-classes. Cela semble plus adapté à votre cas avec les opérations de la pile.

Ce peut être un décorateur de classe qui ajoute une méthode ou deux; Cela a généralement du sens lorsque vous devez transmettre des arguments au décorateur pour affecter la génération de la méthode.

Énoncez votre problème, expliquez vos préoccupations plus grandes en matière de conception, nous pourrons alors nous disputer si quelque chose est contraire à votre modèle.

9000
la source
6

Le linter ignore que vous utilisez une classe en tant que mixin. Pylint sait que vous utilisez un mixin si vous ajoutez le suffixe 'mixin' ou 'Mixin' à la fin du nom de la classe, puis linter cesse de se plaindre.

linter_without_mixins linter2_with_mixin

Les mixins ne sont ni mauvais ni bons en soi, ce n'est qu'un outil. Vous leur faites un bon ou mauvais usage.

dotoscat
la source
2
cela ressemble plus à un commentaire, voir Comment Répondre
Gnat
2
c'est une réponse précieuse parce que je ne pense pas que je saurais quoi que ce soit d'autre à ce sujet
cat le
1

Les mixins sont-ils ok?

Les mixins Python sont-ils un anti-motif?

Les mixins ne sont pas découragés - ils constituent un bon cas d'utilisation pour l'héritage multiple.

Pourquoi votre linter se plaint-il?

Pylint se plaint évidemment parce qu'elle ne sait pas où otherfunc, stacket my_var vient.

Banal?

Il n'y a aucune raison immédiatement apparente de séparer ces deux méthodes en classes parentes distinctes, que ce soit dans votre exemple dans la question ou dans votre exemple lié plus trivial, présenté ici.

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" 

Frais de mixins et de bruit

Le coût de ce que vous faites fait en sorte que votre rapport de bruit soit bruyant. Ce bruit peut masquer des problèmes plus importants avec votre code. C'est un coût important à peser. Un autre coût est que vous séparez votre code interconnecté en différents espaces de noms, ce qui peut compliquer la tâche des programmeurs.

Conclusion

L'héritage permet la réutilisation du code. Si vous obtenez une réutilisation du code avec vos mixins, très bien, ils ont créé pour vous une valeur qui dépasse probablement les autres coûts possibles. Si vous n'obtenez pas la réutilisation / la déduplication de lignes de code, vous n'obtenez probablement pas beaucoup de valeur pour vos mixins, et à ce stade, je pense que le coût du bruit est supérieur aux avantages.

Aaron Hall
la source
Il existe une bonne raison de les séparer dans l'exemple non lié lié: il est plus facile pour moi d'organiser mon code lorsque j'ai une fraction d'une classe complète qui implémente les mathématiques, une autre qui implémente des chaînes, et ainsi de suite, et que vous mélangez pour obtenir la soupe Forth.
Chat
"Un autre coût est que vous séparez votre code en différents espaces de noms, ce qui peut compliquer la tâche des programmeurs pour découvrir la signification de" - non, ce n'est pas le cas du tout. Les espaces de noms de classe facilitent la tâche des groupes de méthodes. Lorsqu'une personne souhaite utiliser la pile, elle doit appeler / implémenter la Soupe Forth, et non les ingrédients individuels.
Cat
@cat probablement parce qu'ils sont artificiels et qu'ils ne sont pas utilisés dans un vrai projet de grande envergure, aucun exemple ne supporte vraiment l'idée d'utiliser mixins. Les deux ne montrent qu'une fois le mixin utilisé, auquel cas il est préférable de mettre en œuvre l'implémentation au même endroit, ce qui est meilleur pour le style et l'organisation. Maintenant, si vous pouviez montrer un cas dans lequel vous voulez des fonctionnalités communes à toutes les classes mais ne voulez pas une classe de base commune, vous utiliseriez un mixin. Ce qui est à côté de votre question de peluche (UltraBird a bien répondu à cette question). Mais cela répond à votre question sur l’applicabilité de mixin.
Dlamblin