1217 votes

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

Dans "Programmation Python", Mark Lutz mentionne "mixin". Je suis à partir d'un C/C++/C# arrière-plan et je n'ai pas entendu ce terme auparavant. Qu'est ce qu'un mixin?

Lire entre les lignes de cet exemple (que j'ai lié, parce que c'est assez long), je suis en supposant que c'est un cas de l'utilisation de l'héritage multiple pour étendre une classe, par opposition à " bon " sous-classement. Est-ce exact? Pourquoi voudrais-je faire plutôt que de mettre la nouvelle fonctionnalité dans une sous-classe? D'ailleurs, pourquoi un mixin/l'héritage multiple approche de mieux que d'utiliser la composition?

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

883voto

Jason Baker Points 56682

Je voudrais vraiment obtenir ma tête autour exactement ce qu'est un mixin est - plus précisément, ce qui sépare un mixin de l'héritage multiple?

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

  1. Vous souhaitez offrir un lot de fonctionnalités en option pour une classe.
  2. Vous souhaitez utiliser une fonction particulière dans beaucoup de classes différentes.

Pour un exemple de numéro un, envisager de werkzeug de la demande et de la réponse du système. Je peux faire un simple vieux objet de demande en disant:

from werkzeug import BaseRequest

class Request(BaseRequest):
    pass

Si je veux ajouter un en-tête accept de soutien, je voudrais faire que

from werkzeug import BaseRequest, AcceptMixin

class Request(BaseRequest, AcceptMixin):
    pass

Si je voulais faire une demande d'objet qui prend en charge accepter les en-têtes, les etags, l'authentification, l'utilisateur et l'agent de soutien, je pourrais faire ceci:

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

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

La différence est subtile, mais dans les exemples ci-dessus, le mixin les classes n'étaient pas faits pour se tenir debout sur leur propre. En plus traditionnelle de l'héritage multiple, La AuthenticationMixin (par exemple) serait probablement quelque chose de plus comme l'Authentificateur. C'est-à-dire, la classe serait probablement être conçu de manière à se tenir debout sur ses propres.

281voto

Randolpho Points 36512

Tout d'abord, vous devez noter que mixin n'existent que dans le multi-héritage langues. Vous ne pouvez pas faire un mixin en Java ou en C#.

Fondamentalement, un mixin est un stand-alone type de base qui fournit des fonctionnalités limitées et polymorphe de résonance pour un enfant de la classe. Si vous êtes en train de penser en C#, pensez à une interface que vous n'avez pas à mettre en œuvre parce que c'est déjà mis en œuvre; vous venez d'hériter d'elle et de bénéficier de ses fonctionnalités.

Mixin sont généralement de portée restreinte et ne doit pas être prolongé.

[edit -- pourquoi:]

Je suppose que je devrais remédier à cela, puisque vous avez demandé. Le grand avantage est que vous n'avez pas à le faire vous-même, encore et encore. En C#, la plus grande place où un mixin pourraient bénéficier de la Cession de modèle. Chaque fois que vous implémentez IDisposable, vous avez presque toujours envie de suivre le même modèle, mais en fin de compte vous écrire et ré-écrire le même code de base avec des variations mineures. Si il y avait un extensible Disposition mixin, vous pouvez vous épargner beaucoup de saisie de texte supplémentaire.

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

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 standard et de l'héritage multiple est juste une question de sémantique; une classe qui a l'héritage multiple peut utiliser un mixin tant que partie intégrante de l'héritage multiple.

Le point d'un mixin est de créer un type qui peut être "mixte" pour tout autre type via l'héritage sans affecter l'héritage type, tout en offrant certains bénéfiques de la fonctionnalité de ce type.

Encore une fois, pensez à une interface qui est déjà mis en œuvre.

Personnellement, je n'utilisez pas de mixin depuis je développe principalement dans une langue qui ne les supporte pas, donc je vais avoir un temps vraiment difficile à venir avec un décent exemple qui sera juste fournir que "ahah!" moment pour vous. Mais je vais essayer de nouveau. Je vais utiliser un exemple qui est artificiel -- la plupart des langues déjà la fonction d'une certaine façon ou d'une autre, mais qui va, espérons-le, expliquez comment mixin sont censés être créés et utilisés. Va ici:

Supposons que vous avez un type que vous voulez être en mesure de sérialiser et de XML. Vous voulez que le type à fournir un "ToXML" méthode qui renvoie une chaîne de caractères contenant un fragment XML avec les données les valeurs de type, et un "FromXML" qui permet le type de reconstituer ses valeurs de données à partir d'un fragment XML dans une chaîne de caractères. Encore une fois, c'est un exemple artificiel, alors peut-être que vous utilisez un fichier de flux, ou un enregistreur XML classe à partir de votre langue est la bibliothèque d'exécution de... quoi que ce soit. Le point est que vous voulez sérialiser vos objets au format XML et d'obtenir un nouvel objet à partir d'XML.

L'autre point important dans cet exemple est que vous voulez le faire de façon générique. Vous ne voulez pas avoir à mettre en œuvre un "ToXML" et "FromXML" méthode pour chaque type que vous souhaitez sérialiser, vous voulez des génériques moyen de s'assurer que votre type de le faire et il fonctionne, tout simplement. Vous voulez réutiliser le code.

Si votre langue prise en charge, vous pouvez créer le XmlSerializable mixin pour faire votre travail pour vous. Ce type de mettre en œuvre les ToXML et la FromXML méthodes. Il serait, à l'aide d'un mécanisme qui n'est pas important pour l'exemple, être capable de recueillir toutes les données nécessaires à partir de n'importe quel type qu'il est mélangé avec de construire le fragment XML renvoyé par ToXML et il serait tout aussi capable de restaurer des données lorsque FromXML est appelé.

Et.. c'est tout. Pour l'utiliser, vous avez un type qui doit être sérialisé en XML hériter de XmlSerializable. Chaque fois que vous avez besoin de sérialiser ou désérialiser ce type, il vous suffit d'appeler ToXML ou FromXML. En fait, depuis XmlSerializable est un véritable type et polymorphe, vous pourrait éventuellement créer un document sérialiseur qui ne sait rien au sujet de votre type d'original, n'acceptant que, disons, un tableau de XmlSerializable types.

Maintenant, imaginez à l'aide de ce scénario pour d'autres choses, comme la création d'un mixin qui veille à ce que chaque classe qu'il mixe dans les journaux pour chaque appel de méthode, ou un mixin qui fournit transactionality le type qu'il mélange. La liste peut continuer encore et encore.

Si vous pensez à une mixin comme un petit type de base est conçu pour ajouter une petite quantité de fonctionnalités à un type sans que cela affecte ce type, alors vous êtes d'or.

Avec de la chance. :)

235voto

Ciro Santilli Points 3341

Cette réponse a pour but d'expliquer mixin avec des exemples qui sont:

  • autonome: bref, sans besoin de connaître toutes les bibliothèques de comprendre l'exemple.

  • en Python, pas dans d'autres langues.

    Il est compréhensible qu'il y avait des exemples dans d'autres langues telles que le Rubis, car le terme est beaucoup plus commun dans ces langues, mais c'est un Python fil.

Il doit également prendre en compte la question controversée:

Est l'héritage multiple ou pas?

Définitions

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

J'ai vu 2 définitions possibles d'un mixin (si ils sont considérés comme différents des autres concepts tels que les classes de base abstraites), et les gens n'ont pas tout à fait d'accord sur ce qui est correct.

Le consensus peut varier entre les différentes langues.

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

Un mixin est une classe telle que certains la 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é à être instancié, mais plutôt servir de classe de base. Sinon, l'instance aurait méthodes qui ne peut pas être appelé sans lever d'exception.

Une contrainte dont certaines sources ajouter, c'est que la classe ne peut pas contenir de données, seulement les méthodes, mais je ne vois pas pourquoi cela est nécessaire. Dans la pratique, cependant, de nombreux mixin n'avez pas toutes les données, et les classes de base sans les données sont plus simples à utiliser.

Un exemple classique est la mise en œuvre de tous les opérateurs de comparaison à partir de seulement <= et ==:

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 en particulier, pourrait avoir été réalisé par l' functools.total_ordering() décorateur, mais le jeu ici est 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: l'héritage multiple

Un mixin est un modèle de conception dans laquelle une méthode d'une classe de base utilise une méthode qu'il ne définit pas, et que la méthode est destinée à être mise en œuvre par une autre classe de base, et non pas par le dérivé comme dans la Définition 1.

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

Il n'est pas facile de décider si une classe donnée est un mixin ou pas: la méthode pourrait être mis en œuvre sur la classe dérivée, dans ce cas, nous sommes de retour à la Définition 1. Vous devez considérer les intentions d'auteur.

Ce modèle est intéressant car il est possible de se recombiner les 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

Autorité Python occurrences

À l' officiel documentatiton pour les collections.abc de la documentation utilise explicitement le terme Mixin Méthodes.

Il stipule que si une classe:

  • implémente __next__
  • hérite d'une classe unique Iterator

ensuite, la classe devient un __iter__ mixin méthode pour gratuit.

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

La documentation pourrait être contradictoire à différents points, et d'autres bibliothèques Python peut-être à l'aide de l'autre définition dans leur documentation.

Cette page utilise également le terme Set mixin, ce qui laisse clairement entendre que des classes comme l' Set et Iterator peut être appelé Mixin classes.

Dans d'autres langues

  • Ruby: de toute évidence, ne nécessite pas l'héritage multiple pour mixin, comme mentionné dans les principaux ouvrages de référence tels que la Programmation Ruby et Le Langage de programmation Ruby

  • C++: Une méthode qui n'est pas mis en œuvre est une méthode virtuelle pure.

    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.

    Définition 2 n'est pas possible, puisqu'il n'est pas possible de mettre en œuvre des méthodes virtuelles pures à partir d'autres classes de base en C++.

45voto

Hamish Downer Points 4086

Je pense à eux comme une discipline de l'utilisation de l'héritage multiple. La façon dont je tiens à les utiliser (et de la façon dont je le voit généralement utilisé dans le Python du monde), c'est que le Mixin

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

De cette façon, il limite le potentiel de la complexité de l'héritage multiple, et il est assez facile de suivre le flux de votre programme en limitant où vous avez à regarder (par rapport à plein de l'héritage multiple).

Si je veux ajouter des variables d'instance (avec plus de souplesse que permis par l'héritage simple), alors j'ai tendance à aller pour la composition.

10voto

bobince Points 270740

Je vous le déconseille mix-ins dans le nouveau code Python, si vous pouvez trouver tout autre moyen de contourner cela (tels que la composition-la place de l'héritage, ou tout simplement de singe brassage des méthodes dans vos propres classes) qui n'est pas beaucoup plus d'efforts.

Dans le vieux-classes de style vous pouvez utiliser les condiments comme un moyen de profiter de quelques méthodes d'une autre classe. Mais dans le nouveau style de tout le monde, même le mix-in, hérite de l' object. Ce qui signifie que toute utilisation de l'héritage multiple naturellement présente MRO questions.

Il y a des façons de faire de multiples-l'héritage des travaux d'entretien en Python, notamment la super fonction de (), mais cela signifie que vous avez à faire de votre ensemble de la hiérarchie de classe à l'aide de super(), et c'est beaucoup plus difficile de comprendre le flux de contrôle.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X