fontsize - python axes label




Qu'est-ce qu'un mixin, et pourquoi sont-ils utiles? (10)

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 certaines langues, le mécanisme d'ajout d'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 qui n'est pas destinée à être instanciée elle-même.

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

Exemple d'héritage multiple

Cet exemple, issu 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 Counter et le OrderedDict du module de collections .

Les deux Counter et OrderedDict sont destinés à être instanciés et utilisés de leur propre chef. 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 bug dans l'un des objets, le fait de le réparer sans précaution pourrait créer un bug dans la sous-classe.

Exemple de Mixin

Les mixins sont généralement promus comme le moyen de réutiliser le 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 deux mixins dans la bibliothèque socketserver .

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

class ThreadingUDPServer(ThreadingMixIn, UDPServer):
    pass

La classe de mélange 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 définition d'objet UDPServer pour autoriser la concurrence.

La méthode surchargée semble être process_request et fournit également une autre méthode, process_request_thread . Ici, il vient 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 contredit

C'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 utilisation:

>>> f1 = Foo('bar')
>>> f2 = Foo()
>>> f1
Foo(foo='bar')
>>> f2
Foo(foo=None)

Dans " Programming Python ", Mark Lutz mentionne "mixins". Je viens d'un arrière-plan C / C ++ / C # et je n'ai pas entendu le terme avant. Qu'est-ce qu'un mixin?

En lisant entre les lignes de cet exemple (auquel j'ai lié parce que c'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 préférable à 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?


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

En D (qui d'ailleurs ne fait pas de MI) ceci est fait en insérant un template (pensez à des macros syntaxiquement conscientes et sûres et vous serez proche) dans une portée. Ceci permet à une seule ligne de code dans une classe, une structure, une fonction, un module ou autre de s'étendre à n'importe quel nombre de déclarations.


J'ai lu que vous avez un arrière-plan ac #. Un bon point de départ pourrait donc être une implémentation de mixin pour .NET.

Vous pouvez consulter le projet codeplex sur http://remix.codeplex.com/

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

ce qui concerne Stefan


Je déconseille les mix-ins 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 juste les méthodes de correction de singe dans vos propres classes) qui n'est pas beaucoup plus effort.

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

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


Je pense qu'il y a eu quelques bonnes explications ici, mais je voulais donner un autre point de vue.

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 fusionnés pour créer un nouveau type de classe à hériter. En substance, vous n'héritez pas de plusieurs classes / mixins, mais plutôt, générez un nouveau type de classe avec toutes les propriétés du mixin à hériter. Cela a du sens puisque Scala est basé sur la JVM où l'héritage multiple n'est pas actuellement supporté (à partir de Java 8). Ce type de classe mixin, en passant, est un type spécial appelé Trait in Scala.

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

Je ne sais pas si l'interpréteur CPython fait la même chose (mixin class-composition) mais je ne serais pas surpris. Aussi, venant d'un fond C ++, je n'appellerais pas un ABC ou une 'interface' équivalente à un mixin - c'est un concept similaire mais divergent dans l'utilisation et l'implémentation.


Je viens d'utiliser un python mixin pour implémenter des tests unitaires pour les pulsations python. Normalement, un milter parle à un MTA, rendant les tests unitaires difficiles. Le test mixin remplace les méthodes qui communiquent avec le MTA et crée un environnement simulé piloté par des scénarios 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 scénarios 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


OP a mentionné qu'il / elle n'a jamais entendu parler de mixin en C ++, peut-être parce qu'ils sont appelés Curlyly Recurring Template Pattern (CRTP) en C ++. En outre, @Ciro Santilli a mentionné que mixin est implémenté via la classe de base abstraite en C ++. Alors que la classe de base abstraite peut être utilisée pour implémenter mixin, elle est excessive car la fonctionnalité de la fonction virtuelle au moment de l'exécution peut être obtenue en utilisant le modèle au moment de la compilation sans la surcharge de la recherche de table virtuelle au moment de l'exécution.

Le motif CRTP est décrit en détail here

J'ai converti l'exemple de python dans la réponse de @Ciro Santilli en C ++ en utilisant la classe template 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));
    }
};

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

    assert (i < j );
    assert (i != j);
    assert (j >  i);
    assert (j >= i);

    return 0;
}

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

Vous pouvez inclure le mixin Comparable et 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 l' instance est plus grande que l' other et plus de 0 si l' other est plus grand.


Tout d'abord, vous devez noter que les mixins existent uniquement dans les langages à plusieurs héritages. Vous ne pouvez pas faire un mixin en Java ou en 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 mixines ont généralement une portée étroite et ne sont pas destinées à être étendues.

[edit - 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 peut être du pattern de disposition . 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 Disposal extensible, vous pourriez vous épargner beaucoup de frappe supplémentaire.

[edit 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 mélange et un héritage multiple standard est juste une question de sémantique; une classe qui a plusieurs héritages peut utiliser un mix dans le cadre de cet héritage multiple.

Le but d'un mixin est de créer un type qui peut être "mélangé" à tout autre type via l'héritage sans affecter le type d'héritage 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 me développe principalement dans un langage qui ne les supporte pas, donc j'ai du mal à trouver un exemple décent qui suffira à fournir ce "ahah!" moment pour vous. Mais je vais essayer à nouveau. Je vais utiliser un exemple qui est artificiel - la plupart des langages fournissent déjà cette fonctionnalité d'une manière ou d'une autre - mais j'espère que cela expliquera comment les mixins sont censés être créés et utilisés. Voici:

Supposons que vous ayez un type que vous voulez pouvoir sérialiser vers et depuis XML. Vous voulez 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 permette 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, alors peut-être que vous utilisez un flux de fichier, ou une classe XML Writer de la bibliothèque d'exécution de votre langage ... peu importe. Le point est que vous voulez sérialiser votre objet en XML et récupérer un nouvel objet à partir de XML.

L'autre point important dans 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 voulez sérialiser, vous voulez des moyens génériques pour vous assurer que votre type va le faire et ça marche. Vous voulez la réutilisation de code.

Si votre langue le supporte, vous pouvez créer le mixin XmlSerializable pour faire votre travail pour vous. Ce type implémenterait les méthodes ToXML et FromXML. Il serait possible, en utilisant un mécanisme qui n'est pas important pour l'exemple, de rassembler toutes les données nécessaires à partir de n'importe quel type avec lequel construire le fragment XML retourné 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 qui doit être sérialisé en XML hérité de XmlSerializable. Chaque fois que vous deviez sérialiser ou désérialiser ce type, vous appeliez 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 document qui ne connaît rien à votre type d'origine, n'acceptant, disons, qu'un tableau de types XmlSerializable.

Imaginons maintenant d'utiliser ce scénario pour d'autres choses, comme créer un mixin qui assure que chaque classe qui le mélange enregistre chaque appel de méthode, ou un mixin qui fournit la transactionnalité au type qui le mélange. La liste peut s'allonger encore et encore.

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.

Espérons. :)


Un mixin est un type spécial d'héritage multiple. Il y a deux situations principales où les mixines sont utilisées:

  1. Vous voulez fournir beaucoup de fonctionnalités optionnelles pour une classe.
  2. Vous voulez utiliser une fonctionnalité particulière dans un grand nombre de 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 objet de requête simple en disant:

from werkzeug import BaseRequest

class Request(BaseRequest):
    pass

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

from werkzeug import BaseRequest, AcceptMixin

class Request(BaseRequest, AcceptMixin):
    pass

Si je voulais créer un objet de requête prenant en charge les en-têtes, les etags, l'authentification et la prise en charge de l'agent utilisateur, je pouvais le faire:

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

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

La différence est subtile, mais dans les exemples ci-dessus, les classes de mixage n'ont pas été conçues pour se suffire à elles-mêmes. Dans l'héritage multiple plus traditionnel, le AuthenticationMixin (par exemple) serait probablement quelque chose de plus comme Authenticator . Autrement dit, la classe serait probablement conçue pour se suffire à elle-même.







mixins