Qu'est-ce qu'un mixin et pourquoi sont-ils utiles?

955

Dans " Programmation Python ", Mark Lutz mentionne les "mixins". Je viens d'un milieu C / C ++ / C # et je n'ai jamais entendu le terme auparavant. Qu'est-ce qu'un mixin?

En lisant entre les lignes de cet exemple (auquel j'ai lié parce qu'il est assez long), je suppose qu'il s'agit d'utiliser l'héritage multiple pour étendre une classe par opposition à un sous-classement «correct». Est-ce correct?

Pourquoi voudrais-je faire cela plutôt que de mettre la nouvelle fonctionnalité dans une sous-classe? D'ailleurs, pourquoi une approche mixin / héritage multiple serait-elle meilleure que l'utilisation de la composition?

Qu'est-ce qui sépare un mixin de l'héritage multiple? Est-ce juste une question de sémantique?

TarkaDaal
la source

Réponses:

710

Un mixin est un type spécial d'héritage multiple. Il existe deux situations principales où des mixins sont utilisés:

  1. Vous souhaitez fournir de nombreuses fonctionnalités optionnelles pour une classe.
  2. Vous souhaitez utiliser une fonctionnalité particulière dans de nombreuses classes différentes.

Pour un exemple de numéro un, considérons le système de demande et de réponse de werkzeug . Je peux créer un ancien objet de requête en disant:

from werkzeug import BaseRequest

class Request(BaseRequest):
    pass

Si je veux ajouter accepter le support d'en-tête, je le ferais

from werkzeug import BaseRequest, AcceptMixin

class Request(AcceptMixin, BaseRequest):
    pass

Si je voulais créer un objet de demande qui prend en charge les en-têtes, les étiquettes, l'authentification et la prise en charge de l'agent utilisateur, je pouvais faire ceci:

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

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

La différence est subtile, mais dans les exemples ci-dessus, les classes de mixage n'ont pas été conçues pour être autonomes. Dans l'héritage multiple plus traditionnel, le AuthenticationMixin(par exemple) serait probablement quelque chose de plus semblable Authenticator. Autrement dit, la classe serait probablement conçue pour être autonome.

Jason Baker
la source
123
Une troisième situation est la suivante: vous souhaitez fournir un grand nombre de fonctionnalités (non facultatives) à une classe, mais vous souhaitez que les fonctionnalités soient dans des classes distinctes (et dans des modules séparés), de sorte que chaque module concerne environ une fonctionnalité (comportement). IOW, non pour la réutilisation, mais pour la compartimentation.
bootchk
60
Probablement pas un problème dans cet exemple, mais vous voulez généralement mettre la classe de base principale comme dernier élément dans la parenthèse afin de créer la chaîne d'héritage: Request ==> Mixin ==> ... ==> BaseRequest. Voir ici: ianlewis.org/en/mixins-and-python
hillel
10
@hillel bon point, mais gardez à l'esprit que Python appellera les méthodes des superclasses de gauche à droite (lorsque vous devez remplacer le constructeur, par exemple).
Eliseu Monar dos Santos
9
Cela ressemble beaucoup au modèle de conception Decorator.
D-Jones
4
Une quatrième situation est la suivante: il existe déjà une famille de Parentclasses et Child1, des sous Child2- ChildNclasses dans une bibliothèque tierce, et vous souhaitez un comportement personnalisé pour toute la famille. Idéalement, vous souhaitez ajouter un tel comportement à Parent, et espérons que le développeur de bibliothèque tiers prendra votre demande Pull. Sinon, vous devrez implémenter la vôtre class NewBehaviorMixin, puis définir un ensemble complet de classes wrapper telles que class NewParent(NewBehaviorMixin, Parent): passand class NewChildN(NewBehaviorMixin, ChildN): pass, etc. (PS: Connaissez-vous une meilleure façon?)
RayLuo
240

Tout d'abord, vous devez noter que les mixins n'existent que dans les langages à héritage multiple. Vous ne pouvez pas faire de mixage en Java ou C #.

Fondamentalement, un mixin est un type de base autonome qui offre une fonctionnalité limitée et une résonance polymorphe pour une classe enfant. Si vous pensez en C #, pensez à une interface que vous n'avez pas à implémenter car elle est déjà implémentée; vous en héritez et profitez de ses fonctionnalités.

Les mixins ont généralement une portée étroite et ne sont pas destinés à être étendus.

[modifier - pourquoi:]

Je suppose que je devrais expliquer pourquoi, puisque vous avez demandé. Le gros avantage est que vous n'avez pas à le faire vous-même encore et encore. En C #, le plus grand endroit où un mixin pourrait bénéficier pourrait être du modèle d'élimination . Chaque fois que vous implémentez IDisposable, vous voulez presque toujours suivre le même modèle, mais vous finissez par écrire et réécrire le même code de base avec des variations mineures. S'il y avait un mixage d'élimination extensible, vous pourriez vous épargner beaucoup de frappe supplémentaire.

[modifier 2 - pour répondre à vos autres questions]

Qu'est-ce qui sépare un mixin de l'héritage multiple? Est-ce juste une question de sémantique?

Oui. La différence entre un mixin et l'héritage multiple standard n'est qu'une question de sémantique; une classe qui a un héritage multiple peut utiliser un mixin dans le cadre de cet héritage multiple.

Le point d'un mixin est de créer un type qui peut être "mélangé" à n'importe quel autre type via l'héritage sans affecter le type héritant tout en offrant des fonctionnalités bénéfiques pour ce type.

Encore une fois, pensez à une interface déjà implémentée.

Personnellement, je n'utilise pas de mixins car je développe principalement dans un langage qui ne les prend pas en charge, donc j'ai vraiment du mal à trouver un exemple décent qui fournira juste ce "ahah!" moment pour vous. Mais je vais réessayer. Je vais utiliser un exemple qui est artificiel - la plupart des langues fournissent déjà la fonctionnalité d'une manière ou d'une autre - mais qui, espérons-le, expliquera comment les mixins sont censés être créés et utilisés. Voici:

Supposons que vous ayez un type que vous souhaitez pouvoir sérialiser vers et depuis XML. Vous souhaitez que le type fournisse une méthode "ToXML" qui renvoie une chaîne contenant un fragment XML avec les valeurs de données du type, et un "FromXML" qui permet au type de reconstruire ses valeurs de données à partir d'un fragment XML dans une chaîne. Encore une fois, ceci est un exemple artificiel, donc vous utilisez peut-être un flux de fichiers ou une classe XML Writer de la bibliothèque d'exécution de votre langue ... peu importe. Le fait est que vous voulez sérialiser votre objet en XML et récupérer un nouvel objet depuis XML.

L'autre point important de cet exemple est que vous voulez le faire de manière générique. Vous ne voulez pas avoir à implémenter une méthode "ToXML" et "FromXML" pour chaque type que vous souhaitez sérialiser, vous voulez des moyens génériques pour vous assurer que votre type le fera et que cela fonctionne. Vous voulez réutiliser le code.

Si votre langue le supportait, vous pourriez créer le mixin XmlSerializable pour faire votre travail pour vous. Ce type implémenterait les méthodes ToXML et FromXML. Il serait, en utilisant un mécanisme qui n'est pas important pour l'exemple, capable de rassembler toutes les données nécessaires à partir de n'importe quel type avec lequel il est mélangé pour créer le fragment XML renvoyé par ToXML et il serait également capable de restaurer ces données lorsque FromXML est appelé.

Et c'est tout. Pour l'utiliser, vous auriez tout type devant être sérialisé en XML hérité de XmlSerializable. Chaque fois que vous avez besoin de sérialiser ou de désérialiser ce type, vous appelez simplement ToXML ou FromXML. En fait, étant donné que XmlSerializable est un type à part entière et polymorphe, vous pourriez concevoir un sérialiseur de documents qui ne sait rien de votre type d'origine, acceptant uniquement, disons, un tableau de types XmlSerializable.

Imaginez maintenant utiliser ce scénario pour d'autres choses, comme la création d'un mixin qui garantit que chaque classe qui le mélange dans les journaux de chaque appel de méthode, ou un mixin qui fournit une transactionnalité au type qui le mélange. La liste peut continuer indéfiniment.

Si vous pensez simplement à un mixin comme un petit type de base conçu pour ajouter une petite quantité de fonctionnalités à un type sans affecter autrement ce type, alors vous êtes en or.

J'espère. :)

Randolpho
la source
25
Hé, vous aimez cette phrase "résonance polymorphe"? Je l'ai inventé moi-même. Je pense. Peut-être que je l'ai entendu quelque part en physique ...
Randolpho
50
Je suis légèrement en désaccord sur votre première phrase. Ruby est un langage à héritage unique et les mixins sont le moyen d'ajouter des méthodes à une classe donnée sans hériter d'une autre classe.
Keltia
23
@Keltia: Je pense que les mixin sont - par définition - à héritage multiple. Dans le cas de Ruby, ils sont un monkeypatch (ou autre chose) pas un bon mixin. Les gens de Ruby peuvent appeler ça un mixin, mais c'est un genre de chose différent.
S.Lott
10
En fait, un vrai mixin ne peut pas utiliser l'héritage multiple. Un mixin comprend des méthodes, des attributs, etc. d'une classe dans une autre sans en hériter. Cela tend à donner les avantages de la réutilisation du code avec le polymorphisme, mais laisse de côté les problèmes de détermination de la filiation (le diamant de la mort, etc.) maintenant).
Trevor
8
Pour mémoire, Java prend désormais en charge les mixins avec des méthodes par défaut.
shmosel
170

Cette réponse vise à expliquer les mixins avec des exemples qui sont:

  • autonome : bref, sans avoir besoin de connaître aucune bibliothèque pour comprendre l'exemple.

  • en Python , pas dans d'autres langues.

    Il est compréhensible qu'il y ait eu des exemples provenant d'autres langages tels que Ruby puisque le terme est beaucoup plus courant dans ces langages, mais il s'agit d'un thread Python .

Il examinera également la question controversée:

L'héritage multiple est-il nécessaire ou non pour caractériser un mixin?

Définitions

Je n'ai pas encore vu de citation d'une source "faisant autorité" disant clairement ce qu'est un mixin en Python.

J'ai vu 2 définitions possibles d'un mixin (si elles doivent être considérées comme différentes d'autres concepts similaires tels que les classes de base abstraites), et les gens ne sont pas entièrement d'accord sur celui qui est correct.

Le consensus peut varier entre différentes langues.

Définition 1: pas d'héritage multiple

Un mixin est une classe telle qu'une méthode de la classe utilise une méthode qui n'est pas définie dans la classe.

Par conséquent, la classe n'est pas destinée à être instanciée, mais plutôt à servir de classe de base. Sinon, l'instance aurait des méthodes qui ne peuvent pas être appelées sans déclencher une exception.

Une contrainte que certaines sources ajoutent est que la classe peut ne pas contenir de données, uniquement des méthodes, mais je ne vois pas pourquoi cela est nécessaire. Cependant, dans la pratique, de nombreux mixins utiles n'ont pas de données et les classes de base sans données sont plus simples à utiliser.

Un exemple classique est l'implémentation de tous les opérateurs de comparaison à partir de <=et uniquement ==:

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 

Cet exemple particulier aurait pu être réalisé via le functools.total_ordering()décorateur, mais le jeu ici était de réinventer la roue:

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)

Définition 2: héritage multiple

Un mixin est un modèle de conception dans lequel une méthode d'une classe de base utilise une méthode qu'elle ne définit pas, et cette méthode est destinée à être implémentée par une autre classe de base , pas par le dérivé comme dans la définition 1.

Le terme classe de mixage fait référence aux classes de base qui sont destinées à être utilisées dans ce modèle de conception (TODO celles qui utilisent la méthode ou celles qui la mettent en œuvre?)

Il n'est pas facile de décider si une classe donnée est un mixin ou non: la méthode pourrait être simplement implémentée sur la classe dérivée, auquel cas nous revenons à la Définition 1. Vous devez considérer les intentions de l'auteur.

Ce schéma est intéressant car il est possible de recombiner des fonctionnalités avec différents choix de classes de base:

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

Occurrences Python faisant autorité

Dans la documentation officielle de collections.abc, la documentation utilise explicitement le terme Méthodes Mixin .

Il indique que si une classe:

  • met en oeuvre __next__
  • hérite d'une seule classe Iterator

alors la classe obtient gratuitement une __iter__ méthode de mixage .

Par conséquent, au moins sur ce point de la documentation, mixin ne nécessite pas d'héritage multiple et est cohérent avec la définition 1.

La documentation pourrait bien sûr être contradictoire à différents points, et d'autres bibliothèques Python importantes pourraient utiliser l'autre définition dans leur documentation.

Cette page utilise également le terme Set mixin, ce qui suggère clairement que des classes comme SetetIterator peuvent être appelées classes Mixin.

Dans d'autres langues

  • Ruby: Clairement ne nécessite pas d'héritage multiple pour mixin, comme mentionné dans les principaux livres de référence tels que Programming Ruby et The Ruby Programming Language

  • C ++: Une méthode qui n'est pas implémentée est une méthode virtuelle pure.

    La définition 1 coïncide avec la définition d'une classe abstraite (une classe qui a une méthode virtuelle pure). Cette classe ne peut pas être instanciée.

    La définition 2 est possible avec l'héritage virtuel: héritage multiple de deux classes dérivées

Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功
la source
37

Je les considère comme une manière disciplinée d'utiliser l'héritage multiple - parce qu'en fin de compte, un mixin n'est qu'une autre classe python qui (pourrait) suivre les conventions sur les classes appelées mixins.

Ma compréhension des conventions qui régissent ce que vous appelleriez un Mixin est qu'un Mixin:

  • ajoute des méthodes mais pas des variables d'instance (les constantes de classe sont OK)
  • hérite uniquement de object(en Python)

De cette façon, il limite la complexité potentielle de l'héritage multiple et facilite le suivi du flux de votre programme en limitant où vous devez regarder (par rapport à l'héritage multiple complet). Ils sont similaires aux modules rubis .

Si je veux ajouter des variables d'instance (avec plus de flexibilité que ne le permet l'héritage unique), j'ai tendance à opter pour la composition.

Cela dit, j'ai vu des classes appelées XYZMixin qui ont des variables d'instance.

Hamish Downer
la source
30

Mixins est un concept de programmation dans lequel la classe fournit des fonctionnalités mais n'est pas destiné à être utilisé pour l'instanciation. L'objectif principal de Mixins est de fournir des fonctionnalités autonomes et il serait préférable que les mixins eux-mêmes n'aient pas d'héritage avec d'autres mixins et évitent également l'état. Dans des langages tels que Ruby, il existe une prise en charge directe des langues, mais pour Python, ce n'est pas le cas. Cependant, vous pouvez utiliser l'héritage multi-classes pour exécuter les fonctionnalités fournies en Python.

J'ai regardé cette vidéo http://www.youtube.com/watch?v=v_uKI2NOLEM pour comprendre les bases des mixins. Il est très utile pour un débutant de comprendre les bases des mixins et comment ils fonctionnent et les problèmes que vous pourriez rencontrer lors de leur mise en œuvre.

Wikipedia est toujours le meilleur: http://en.wikipedia.org/wiki/Mixin

Lakesh
la source
29

Qu'est-ce qui sépare un mixin de l'héritage multiple? Est-ce juste une question de sémantique?

Un mixin est une forme limitée d'héritage multiple. Dans certains langages, le mécanisme pour ajouter un mixin à une classe est légèrement différent (en termes de syntaxe) de celui de l'héritage.

Dans le contexte de Python en particulier, un mixin est une classe parente qui fournit des fonctionnalités aux sous-classes mais n'est pas destinée à être instanciée elle-même.

Ce qui pourrait vous amener à dire, "c'est juste un héritage multiple, pas vraiment un mixage", c'est si la classe qui pourrait être confondue pour un mixage peut en fait être instanciée et utilisée - donc c'est en fait une différence sémantique et très réelle.

Exemple d'héritage multiple

Cet exemple, extrait de la documentation , est un 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),)

Il sous-classe à la fois le Counteret le OrderedDictdu collectionsmodule.

Les deux Counteret OrderedDictsont destinés à être instanciés et utilisés seuls. Cependant, en les sous-classant tous les deux, nous pouvons avoir un compteur qui est ordonné et réutilise le code dans chaque objet.

C'est un moyen puissant de réutiliser le code, mais cela peut aussi être problématique. S'il s'avère qu'il y a un bogue dans l'un des objets, le corriger sans soin pourrait créer un bogue dans la sous-classe.

Exemple de mixin

Les mixins sont généralement présentés comme le moyen d'obtenir la réutilisation du code sans problèmes de couplage potentiels que l'héritage multiple coopératif, comme le OrderedCounter, pourrait avoir. Lorsque vous utilisez des mixins, vous utilisez des fonctionnalités qui ne sont pas aussi étroitement couplées aux données.

Contrairement à l'exemple ci-dessus, un mixin n'est pas destiné à être utilisé seul. Il fournit des fonctionnalités nouvelles ou différentes.

Par exemple, la bibliothèque standard a quelques mixins dans la socketserverbibliothèque .

Les versions de fork et de threading de chaque type de serveur peuvent être créées à l'aide de ces classes mixtes. Par exemple, ThreadingUDPServer est créé comme suit:

class ThreadingUDPServer(ThreadingMixIn, UDPServer):
    pass

La classe mix-in vient en premier, car elle remplace une méthode définie dans UDPServer. La définition des différents attributs modifie également le comportement du mécanisme de serveur sous-jacent.

Dans ce cas, les méthodes mixin remplacent les méthodes de la UDPServerdéfinition d'objet pour autoriser la concurrence.

La méthode surchargée semble être process_requestet il fournit également une autre méthode, process_request_thread. Le voici à partir du code source :

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

Un exemple artificiel

Ceci est un mixin qui est principalement à des fins de démonstration - la plupart des objets évolueront au-delà de l'utilité de cette repr:

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

et l'utilisation serait:

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

Et l'utilisation:

>>> f1 = Foo('bar')
>>> f2 = Foo()
>>> f1
Foo(foo='bar')
>>> f2
Foo(foo=None)
Aaron Hall
la source
11

Je pense qu'il y a eu de bonnes explications ici, mais je voulais apporter une autre perspective.

Dans Scala, vous pouvez faire des mixins comme cela a été décrit ici, mais ce qui est très intéressant, c'est que les mixins sont en fait «fusionnés» ensemble pour créer un nouveau type de classe dont il peut hériter. En substance, vous n'héritez pas de plusieurs classes / mixins, mais générez plutôt un nouveau type de classe avec toutes les propriétés du mixin dont vous devez hériter. Cela est logique puisque Scala est basé sur la JVM où l'héritage multiple n'est pas actuellement pris en charge (à partir de Java 8). Ce type de classe mixin, soit dit en passant, est un type spécial appelé Trait in Scala.

Il est fait allusion à la façon dont une classe est définie: la classe NewClass étend FirstMixin avec SecondMixin avec ThirdMixin ...

Je ne sais pas si l'interpréteur CPython fait de même (mixin class-composition) mais je ne serais pas surpris. De plus, venant d'un arrière-plan C ++, je n'appellerais pas un ABC ou une `` interface '' équivalente à un mixin - c'est un concept similaire mais divergent dans l'utilisation et la mise en œuvre.

SilentDirge
la source
9

Je déconseille les mélanges dans le nouveau code Python, si vous pouvez trouver un autre moyen de le contourner (comme la composition au lieu de l'héritage, ou simplement des méthodes de correction de singe dans vos propres classes) qui ne sont pas beaucoup plus effort.

Dans les classes à l'ancienne, vous pouvez utiliser des mix-ins pour récupérer quelques méthodes d'une autre classe. Mais dans le monde du nouveau style, tout, même le mixage, hérite de object. Cela signifie que toute utilisation de l'héritage multiple introduit naturellement des problèmes de MRO .

Il existe des moyens de faire fonctionner MRO à héritage multiple en Python, notamment la fonction super (), mais cela signifie que vous devez faire toute la hiérarchie de votre classe en utilisant super (), et il est beaucoup plus difficile de comprendre le flux de contrôle.

bobince
la source
3
Depuis la version 2.3, Python utilise la "résolution de méthode C3" expliquée dans l' Ordre de résolution de méthode Python 2.3 ou Ordre de résolution de méthode .
webwurst
11
Personnellement, je préférerais les mixins au patch de singe dans la plupart des cas; il est plus facile de raisonner et de suivre le code.
tdammers
5
Voté. Bien que votre réponse exprime une opinion valable sur les styles de développement, vous ne répondez pas vraiment à la question réelle.
Ryan
8

Peut-être que quelques exemples vous aideront.

Si vous créez une classe et que vous souhaitez qu'elle agisse comme un dictionnaire, vous pouvez définir toutes les différentes __ __méthodes nécessaires. Mais c'est un peu pénible. Vous pouvez également en définir quelques-uns et hériter (en plus de tout autre héritage) de UserDict.DictMixin(déplacé verscollections.DictMixin dans py3k). Cela aura pour effet de définir automatiquement tout le reste de l'api du dictionnaire.

Un deuxième exemple: la boîte à outils GUI wxPython vous permet de créer des contrôles de liste avec plusieurs colonnes (comme, par exemple, l'affichage du fichier dans l'Explorateur Windows). Par défaut, ces listes sont assez basiques. Vous pouvez ajouter des fonctionnalités supplémentaires, telles que la possibilité de trier la liste par une colonne particulière en cliquant sur l'en-tête de la colonne, en héritant de ListCtrl et en ajoutant des mixins appropriés.

John Fouhy
la source
8

Ce n'est pas un exemple Python mais dans le langage de programmation D, le terme mixinest utilisé pour désigner une construction utilisée de la même manière; ajouter un tas de trucs à une classe.

Dans D (qui d'ailleurs ne fait pas MI), cela se fait en insérant un modèle (pensez aux macros syntaxiquement conscientes et sûres et vous serez proche) dans une portée. Cela permet à une seule ligne de code dans une classe, une structure, une fonction, un module ou quoi que ce soit d'étendre à n'importe quel nombre de déclarations.

BCS
la source
2
Mixin est un terme général, utilisé en D, Ruby, etc. Selon Wikipedia, ils sont originaires des systèmes de lisp de la vieille école et ont été documentés pour la première fois en 1983: en.wikipedia.org/wiki/…
Lee B
7

OP mentionne qu'il / elle n'a jamais entendu parler de mixin en C ++, peut-être parce qu'ils sont appelés Curieusement Recurrent Template Pattern (CRTP) en C ++. De plus, @Ciro Santilli a mentionné que le mixin est implémenté via une classe de base abstraite en C ++. Bien que la classe de base abstraite puisse être utilisée pour implémenter le mixage, c'est une exagération car la fonctionnalité de la fonction virtuelle au moment de l'exécution peut être obtenue en utilisant un modèle au moment de la compilation sans la surcharge de la recherche de table virtuelle au moment de l'exécution.

Le modèle CRTP est décrit en détail ici

J'ai converti l'exemple python dans la réponse de @Ciro Santilli en C ++ en utilisant la classe de modèle ci-dessous:

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

EDIT: Ajout d'un constructeur protégé dans ComparableMixin afin qu'il ne puisse être hérité que et non instancié. Mise à jour de l'exemple pour montrer comment le constructeur protégé provoquera une erreur de compilation lorsqu'un objet de ComparableMixin est créé.

bigdata2
la source
Mixins et CRTP ne sont pas exactement la même chose en C ++.
ashrasmun
6

Peut-être qu'un exemple de rubis peut aider:

Vous pouvez inclure le mixin Comparableet définir une fonction "<=>(other)", le mixin fournit toutes ces fonctions:

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

Il le fait en invoquant <=>(other) et en redonnant le bon résultat.

"instance <=> other"renvoie 0 si les deux objets sont égaux, moins de 0 si instanceest plus grand que otheret plus de 0 si otherest plus grand.

Georg Schölly
la source
Voici un article fournissant un mixin similaire pour Python. Bien que la suggestion se définisse __lt__comme base au lieu de __cmp__, cette dernière est en fait obsolète et déconseillée à utiliser. Il me semble plus simple d'utiliser ce mix au lieu de décorateurs assez compliqués (faisant partie de functools ) - bien que celui-ci puisse réagir plus dynamiquement sur quelles comparaisons sont fournies ...
Tobias Kienzler
6

mixin donne un moyen d'ajouter des fonctionnalités dans une classe, c'est-à-dire que vous pouvez interagir avec les méthodes définies dans un module en incluant le module dans la classe souhaitée. Bien que ruby ​​ne supporte pas l'héritage multiple, mais fournit une mixin comme alternative pour y parvenir.

voici un exemple qui explique comment l'héritage multiple est réalisé en utilisant mixin.

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
la source
4
Quelle est la différence entre cela et l'héritage multiple en général?
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功
La différence est que vous n'êtes pas en mesure de créer des instances à partir de modules, mais s'il n'y a pas de différenciation entre les classes générales et les modules, les mixins ne sont pas explicites et il est difficile de comprendre où est une classe générale et où est un mixin
ka8725
Donc, dans Ruby, les mixins ne sont que des classes qui ne peuvent pas être instanciées mais doivent être utilisées pour l'héritage multiple?
Trilarion
6

Je viens d'utiliser un mixin python pour implémenter des tests unitaires pour les milters python. Normalement, une discussion plus douce avec un MTA rend les tests unitaires difficiles. Le mixage de test remplace les méthodes qui communiquent avec le MTA et créent à la place un environnement simulé piloté par des cas de test.

Donc, vous prenez une application Milter non modifiée, comme spfmilter, et mixin TestBase, comme ceci:

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)

Ensuite, utilisez TestMilter dans les cas de test pour l'application Milter:

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
la source
4

Je pense que les réponses précédentes ont très bien défini ce que sont les MixIns . Cependant, afin de mieux les comprendre, il pourrait être utile de comparer les MixIns avec les classes abstraites et les interfaces du point de vue code / implémentation:

1. Classe abstraite

  • Classe qui doit contenir une ou plusieurs méthodes abstraites

  • La classe abstraite peut contenir des méthodes d'état (variables d'instance) et non abstraites

2. Interface

  • L'interface contient uniquement des méthodes abstraites (pas de méthodes non abstraites et pas d'état interne)

3. MixIns

  • Les MixIns (comme les Interfaces) ne contiennent pas d'état interne (variables d'instance)
  • Les MixIns contiennent une ou plusieurs méthodes non abstraites (elles peuvent contenir des méthodes non abstraites contrairement aux interfaces)

Dans Python par exemple, ce ne sont que des conventions, car tout ce qui précède est défini comme classes. Cependant, la caractéristique commune des classes abstraites, des interfaces et des MixIns est qu'elles ne devraient pas exister par elles-mêmes, c'est-à-dire qu'elles ne devraient pas être instanciées.

Tomasz Bartkowiak
la source
3

J'ai lu que vous avez des antécédents en AC #. Un bon point de départ pourrait donc être une implémentation mixin pour .NET.

Vous voudrez peut-être consulter le projet codeplex à http://remix.codeplex.com/

Regardez le lien du symposium lang.net pour obtenir un aperçu. Il y a encore plus à venir sur la documentation sur la page codeplex.

en ce qui concerne Stefan

Stefan Papp
la source