Che cos'è un mixin e perché sono utili?


955

In " Programmazione di Python ", Mark Lutz menziona "mixin". Vengo da un background C / C ++ / C # e non ho mai sentito il termine prima. Che cos'è un mixin?

Leggendo tra le righe di questo esempio (che ho collegato perché è piuttosto lungo), presumo sia un caso di utilizzo dell'ereditarietà multipla per estendere una classe anziché una sottoclasse "corretta". È giusto?

Perché dovrei voler farlo piuttosto che mettere la nuova funzionalità in una sottoclasse? Del resto, perché un approccio mixin / ereditarietà multipla sarebbe meglio dell'uso della composizione?

Cosa separa un mixin dall'eredità multipla? È solo una questione di semantica?

Risposte:


710

Un mixin è un tipo speciale di eredità multipla. Esistono due situazioni principali in cui vengono utilizzati i mixin:

  1. Vuoi fornire molte funzionalità opzionali per una classe.
  2. Volete usare una caratteristica particolare in molte classi diverse.

Per un esempio del numero uno, prendere in considerazione il sistema di richiesta e risposta di werkzeug . Posso creare un semplice oggetto di richiesta dicendo:

from werkzeug import BaseRequest

class Request(BaseRequest):
    pass

Se voglio aggiungere accettare il supporto dell'intestazione, lo farei

from werkzeug import BaseRequest, AcceptMixin

class Request(AcceptMixin, BaseRequest):
    pass

Se volessi creare un oggetto richiesta che supporta accettare intestazioni, etichette, autenticazione e supporto dell'agente utente, potrei farlo:

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

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

La differenza è sottile, ma negli esempi sopra, le classi di mixin non sono state fatte per conto loro. In eredità multipla più tradizionale, il AuthenticationMixin(per esempio) sarebbe probabilmente qualcosa di più simile Authenticator. Cioè, la classe sarebbe probabilmente progettata per essere autonoma.


123
Una terza situazione è: vuoi fornire molte funzionalità (non opzionali) per una classe, ma vuoi le funzionalità in classi separate (e in moduli separati), quindi ogni modulo ha a che fare con una caratteristica (comportamento). IOW, non per il riutilizzo, ma per compartimentazione.
Bootchk,

60
Probabilmente non è un problema in questo esempio, ma in genere si desidera inserire la classe base principale come ultimo elemento tra parentesi in modo da creare la catena di ereditarietà: Request ==> Mixin ==> ... ==> BaseRequest. Vedi qui: ianlewis.org/en/mixins-and-python
hillel

10
@hillel buon punto, ma tieni presente che Python chiamerà i metodi delle superclassi da sinistra a destra (ad esempio quando devi scavalcare il costruttore).
Eliseu Monar dos Santos,

9
Sembra molto simile al modello di design di Decorator.
D-Jones,

4
Una quarta situazione è: c'è già una famiglia esistente di Parentclasse e Child1, Child2, ChildNsottoclassi all'interno di una libreria di terze parti, e si desidera un comportamento su misura per tutta la famiglia. Idealmente, desideri aggiungere tale comportamento Parente speri che lo sviluppatore della libreria di terze parti accetti la tua richiesta pull. Altrimenti dovrai implementare il tuo class NewBehaviorMixin, e quindi definire un set completo di classi wrapper come class NewParent(NewBehaviorMixin, Parent): passe class NewChildN(NewBehaviorMixin, ChildN): pass, ecc. (PS: conosci un modo migliore?)
RayLuo,

240

Innanzitutto, dovresti notare che i mixin esistono solo in linguaggi con ereditarietà multipla. Non puoi fare un mixin in Java o C #.

Fondamentalmente, un mixin è un tipo di base autonomo che fornisce funzionalità limitate e risonanza polimorfica per una classe figlio. Se stai pensando in C #, pensa a un'interfaccia che non devi effettivamente implementare perché è già implementata; ne erediti e ne trarrai vantaggio.

I mixin hanno in genere un ambito ristretto e non intendono essere estesi.

[modifica - sul perché:]

Suppongo che dovrei affrontare il perché, dal momento che hai chiesto. Il grande vantaggio è che non devi farlo più e più volte da solo. In C #, il luogo più grande in cui un mixin potrebbe beneficiare potrebbe essere il modello di smaltimento . Ogni volta che si implementa IDisposable, si desidera quasi sempre seguire lo stesso modello, ma si finisce per scrivere e riscrivere lo stesso codice di base con variazioni minori. Se ci fosse un mixin di smaltimento estensibile, potresti risparmiare un sacco di digitazione extra.

[modifica 2 - per rispondere alle altre tue domande]

Cosa separa un mixin dall'eredità multipla? È solo una questione di semantica?

Sì. La differenza tra un mixin e l'eredità multipla standard è solo una questione di semantica; una classe che ha eredità multipla potrebbe utilizzare un mixin come parte di tale eredità multipla.

Il punto di un mixin è quello di creare un tipo che può essere "mischiato" a qualsiasi altro tipo tramite ereditarietà senza influire sul tipo ereditario, offrendo comunque alcune funzionalità utili per quel tipo.

Ancora una volta, pensa a un'interfaccia già implementata.

Personalmente non uso i mixin poiché mi sviluppo principalmente in un linguaggio che non li supporta, quindi sto attraversando un momento davvero difficile trovare un esempio decente che fornirà semplicemente "ahah!" momento per te. Ma ci riproverò. Userò un esempio che è inventato - la maggior parte delle lingue fornisce già la funzionalità in un modo o nell'altro - ma questo, si spera, spiegherà come si suppone che i mixin debbano essere creati e usati. Ecco qui:

Supponiamo di avere un tipo che si desidera poter serializzare da e verso XML. Si desidera che il tipo fornisca un metodo "ToXML" che restituisce una stringa contenente un frammento XML con i valori di dati del tipo e un "FromXML" che consente al tipo di ricostruire i suoi valori di dati da un frammento XML in una stringa. Ancora una volta, questo è un esempio inventato, quindi forse usi un flusso di file o una classe Writer XML dalla libreria di runtime della tua lingua ... qualunque cosa. Il punto è che vuoi serializzare il tuo oggetto su XML e recuperare un nuovo oggetto da XML.

L'altro punto importante in questo esempio è che si desidera farlo in modo generico. Non vuoi implementare un metodo "ToXML" e "FromXML" per ogni tipo che vuoi serializzare, vuoi alcuni mezzi generici per assicurarti che il tuo tipo lo faccia e funzioni. Vuoi riutilizzare il codice.

Se la tua lingua lo supporta, puoi creare il mixin XmlSerializable per fare il tuo lavoro per te. Questo tipo implementerebbe i metodi ToXML e FromXML. Usando un meccanismo che non è importante per l'esempio, sarebbe in grado di raccogliere tutti i dati necessari da qualsiasi tipo in cui è mescolato per costruire il frammento XML restituito da ToXML e sarebbe ugualmente in grado di ripristinare quei dati quando FromXML è chiamato.

E .. questo è tutto. Per usarlo, dovresti avere qualsiasi tipo che deve essere serializzato su XML ereditato da XmlSerializable. Ogni volta che dovevi serializzare o deserializzare quel tipo, chiameresti semplicemente ToXML o FromXML. Infatti, poiché XmlSerializable è un tipo a tutti gli effetti e polimorfico, è possibile concepire un serializzatore di documenti che non conosca nulla del tipo originale, accettando solo, diciamo, una matrice di tipi XmlSerializable.

Ora immagina di usare questo scenario per altre cose, come la creazione di un mixin che assicuri che ogni classe che lo mescoli registri ogni chiamata di metodo, o un mixin che fornisca transazionalità al tipo in cui lo mescola. L'elenco può continuare all'infinito.

Se pensi a un mixin come a un piccolo tipo di base progettato per aggiungere una piccola quantità di funzionalità a un tipo senza influire su quel tipo, allora sei d'oro.

Fiduciosamente. :)


25
Ehi, ti piace quella frase "risonanza polimorfica"? L'ho inventato io. Penso. Forse l'ho sentito in fisica da qualche parte ...
Randolpho,

50
Non sono leggermente d'accordo sulla tua prima frase. Ruby è un linguaggio a eredità singola e i mixin sono il modo per aggiungere metodi a una data classe senza ereditare da un'altra classe.
Keltia,

23
@Keltia: Penso che i mixin siano - per definizione - ereditarietà multipla. Nel caso Ruby, sono un monkeypatch (o qualcos'altro) non un vero mixin. La gente di Ruby può chiamarlo un mixin, ma è un tipo diverso di cose.
S.Lott

10
In realtà, un vero mixin non può usare l'ereditarietà multipla. Un mixin include metodi, attributi, ecc. Da una classe in un'altra senza ereditarla. Questo tende a dare i vantaggi del riutilizzo del codice con il polimorfismo, ma esclude i problemi che determinano la parentela (il diamante della morte, ecc.) I linguaggi di supporto del mixin tendono anche a consentire l'inclusione parziale della classe di mixin (le cose stanno iniziando a sembrare un po 'come aspetti ora).
Trevor,

8
Per la cronaca, Java ora supporta i mixin con metodi predefiniti.
shmosel,

170

Questa risposta ha lo scopo di spiegare i mixin con esempi che sono:

  • autonomo : breve, senza bisogno di conoscere alcuna libreria per capire l'esempio.

  • in Python , non in altre lingue.

    È comprensibile che esistessero esempi da altre lingue come Ruby poiché il termine è molto più comune in quelle lingue, ma questo è un thread Python .

Dovrà inoltre considerare la controversa domanda:

L'ereditarietà multipla è necessaria o no per caratterizzare un mixin?

definizioni

Devo ancora vedere una citazione da una fonte "autorevole" che dice chiaramente cos'è un mixin in Python.

Ho visto 2 possibili definizioni di un mixin (se devono essere considerate diverse da altri concetti simili come le classi di base astratte) e le persone non sono completamente d'accordo su quale sia corretta.

Il consenso può variare tra lingue diverse.

Definizione 1: nessuna eredità multipla

Un mixin è una classe tale che un metodo della classe utilizza un metodo che non è definito nella classe.

Pertanto la classe non è pensata per essere istanziata, ma piuttosto funge da classe base. Altrimenti l'istanza avrebbe metodi che non possono essere chiamati senza sollevare un'eccezione.

Un vincolo che alcune fonti aggiungono è che la classe potrebbe non contenere dati, solo metodi, ma non vedo perché questo sia necessario. In pratica, tuttavia, molti utili mixin non hanno dati e le classi base senza dati sono più semplici da usare.

Un classico esempio è l'implementazione di tutti gli operatori di confronto da soli <=e ==:

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 

Questo esempio particolare avrebbe potuto essere realizzato tramite il functools.total_ordering()decoratore, ma il gioco qui doveva reinventare la ruota:

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)

Definizione 2: eredità multipla

Un mixin è un modello di progettazione in cui un metodo di una classe base utilizza un metodo che non definisce e tale metodo è pensato per essere implementato da un'altra classe base , non dal derivato come nella Definizione 1.

Il termine classe mixin si riferisce alle classi base che sono destinate ad essere utilizzate in quel modello di progettazione (TODO quelli che usano il metodo o quelli che lo implementano?)

Non è facile decidere se una determinata classe è un mixin oppure no: il metodo potrebbe essere semplicemente implementato sulla classe derivata, nel qual caso torniamo alla Definizione 1. Devi considerare le intenzioni dell'autore.

Questo modello è interessante perché è possibile ricombinare funzionalità con diverse scelte di classi 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

Occorrenze autorevoli di Python

Nel documentatiton ufficiale per collections.abc la documentazione usa esplicitamente il termine Metodi Mixin .

Indica che se una classe:

  • attrezzi __next__
  • eredita da una singola classe Iterator

quindi la classe ottiene un __iter__ metodo mixin gratuitamente.

Pertanto, almeno su questo punto della documentazione, il mixin non richiede l'ereditarietà multipla ed è coerente con la definizione 1.

La documentazione potrebbe ovviamente essere contraddittoria in diversi punti e altre importanti librerie Python potrebbero usare l'altra definizione nella loro documentazione.

Questa pagina usa anche il termine Set mixin, che suggerisce chiaramente che alle classi piacciono Sete Iteratorpossono essere chiamate classi Mixin.

In altre lingue

  • Ruby: Chiaramente non richiede l'ereditarietà multipla per il mixin, come menzionato nei principali libri di riferimento come Programming Ruby e The Ruby Programming Language

  • C ++: un metodo non implementato è un metodo virtuale puro.

    La definizione 1 coincide con la definizione di una classe astratta (una classe che ha un metodo virtuale puro). Quella classe non può essere istanziata.

    La definizione 2 è possibile con l'ereditarietà virtuale: Ereditarietà multipla da due classi derivate


37

Li considero un modo disciplinato di usare l'ereditarietà multipla, perché alla fine un mixin è solo un'altra classe di pitone che (potrebbe) seguire le convenzioni sulle classi che si chiamano mixin.

La mia comprensione delle convenzioni che regolano qualcosa che tu chiameresti un Mixin è che un Mixin:

  • aggiunge metodi ma non variabili di istanza (le costanti di classe sono OK)
  • eredita solo da object(in Python)

In questo modo limita la potenziale complessità dell'ereditarietà multipla e rende ragionevolmente facile tenere traccia del flusso del programma limitando il punto in cui devi guardare (rispetto all'ereditarietà multipla completa). Sono simili ai moduli ruby .

Se voglio aggiungere variabili di istanza (con una maggiore flessibilità rispetto a quella consentita dalla singola eredità), tendo ad optare per la composizione.

Detto questo, ho visto classi chiamate XYZMixin che hanno variabili di istanza.


30

Mixins è un concetto di programmazione in cui la classe fornisce funzionalità ma non è pensata per essere utilizzata per l'istanza. Lo scopo principale dei Mixin è quello di fornire funzionalità che siano indipendenti e sarebbe meglio se i Mixin stessi non avessero eredità con altri Mixin ed evitassero anche lo stato. In lingue come Ruby, esiste un supporto linguistico diretto, ma per Python non esiste. Tuttavia, è possibile utilizzare l'ereditarietà multi-classe per eseguire la funzionalità fornita in Python.

Ho visto questo video http://www.youtube.com/watch?v=v_uKI2NOLEM per capire le basi dei mixin. È abbastanza utile per un principiante capire le basi dei mixin e come funzionano e i problemi che potresti dover affrontare per implementarli.

Wikipedia è ancora il migliore: http://en.wikipedia.org/wiki/Mixin


29

Cosa separa un mixin dall'eredità multipla? È solo una questione di semantica?

Un mixin è una forma limitata di eredità multipla. In alcune lingue il meccanismo per aggiungere un mixin a una classe è leggermente diverso (in termini di sintassi) da quello dell'ereditarietà.

Soprattutto nel contesto di Python, un mixin è una classe genitore che fornisce funzionalità alle sottoclassi ma non intende essere istanziato da solo.

Ciò che potrebbe farti dire, "questa è solo un'eredità multipla, non proprio un mixin" è se la classe che potrebbe essere confusa per un mixin può effettivamente essere istanziata e utilizzata - quindi in effetti è una differenza semantica e molto reale.

Esempio di ereditarietà multipla

Questo esempio, dalla documentazione , è 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),)

E sottoclassi sia l' Countere OrderedDictdal collectionsmodulo.

Entrambi Countere OrderedDictsono destinati a essere istanziati e utilizzati da soli. Tuttavia, eseguendo la sottoclasse di entrambi, è possibile disporre di un contatore ordinato che riutilizzi il codice in ciascun oggetto.

Questo è un modo efficace per riutilizzare il codice, ma può anche essere problematico. Se si scopre che c'è un bug in uno degli oggetti, risolverlo senza cura potrebbe creare un bug nella sottoclasse.

Esempio di un mixin

I mixin sono di solito promossi come il modo per ottenere il riutilizzo del codice senza potenziali problemi di accoppiamento che l'ereditarietà multipla cooperativa, come OrderedCounter, potrebbe avere. Quando si utilizzano i mixin, si utilizzano funzionalità non strettamente associate ai dati.

A differenza dell'esempio sopra, un mixin non è destinato a essere usato da solo. Fornisce funzionalità nuove o diverse.

Ad esempio, la libreria standard ha un paio di mixin nella socketserverlibreria .

Le versioni fork e threading di ciascun tipo di server possono essere create utilizzando queste classi di mix-in. Ad esempio, ThreadingUDPServer viene creato come segue:

class ThreadingUDPServer(ThreadingMixIn, UDPServer):
    pass

La classe di mix-in viene prima di tutto, poiché ignora un metodo definito in UDPServer. L'impostazione dei vari attributi modifica anche il comportamento del meccanismo del server sottostante.

In questo caso, i metodi mixin sovrascrivono i metodi nella UDPServerdefinizione dell'oggetto per consentire la concorrenza.

Il metodo override appare process_requeste fornisce anche un altro metodo, process_request_thread. Eccolo dal codice sorgente :

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 esempio contrastato

Questo è un mixin che è principalmente a scopo dimostrativo - la maggior parte degli oggetti si evolverà oltre l'utilità di questo 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)
          )

e l'utilizzo sarebbe:

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

E utilizzo:

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

11

Penso che ci siano state alcune buone spiegazioni qui, ma volevo fornire un'altra prospettiva.

In Scala, puoi fare mixin come è stato descritto qui, ma ciò che è molto interessante è che i mixin sono in realtà "fusi" insieme per creare un nuovo tipo di classe da ereditare. In sostanza, non erediti da più classi / mixin, ma piuttosto, generi un nuovo tipo di classe con tutte le proprietà del mixin da cui ereditare. Ciò ha senso dal momento che Scala si basa sulla JVM in cui l'ereditarietà multipla non è attualmente supportata (a partire da Java 8). Questo tipo di classe di mixin, a proposito, è un tipo speciale chiamato Trait in Scala.

Viene suggerito il modo in cui viene definita una classe: la classe NewClass estende FirstMixin con SecondMixin con ThirdMixin ...

Non sono sicuro che l'interprete CPython faccia lo stesso (composizione di classe mixin) ma non sarei sorpreso. Inoltre, proveniente da un background C ++, non definirei un ABC o "interfaccia" equivalente a un mixin: è un concetto simile ma divergente nell'uso e nell'implementazione.


9

Vorrei sconsigliare i mix-in nel nuovo codice Python, se riesci a trovare un altro modo per aggirarlo (come composizione-anziché-ereditarietà o semplicemente metodi di patching delle scimmie nelle tue classi) che non è molto di più sforzo.

Nelle classi vecchio stile potresti usare i mix-in come un modo per prendere alcuni metodi da un'altra classe. Ma nel mondo nuovo stile tutto, anche il mix-in, eredita da object. Ciò significa che qualsiasi utilizzo dell'ereditarietà multipla introduce naturalmente problemi di MRO .

Ci sono modi per far funzionare MRO con ereditarietà multipla in Python, in particolare la funzione super (), ma significa che devi fare l'intera gerarchia di classi usando super (), ed è considerevolmente più difficile capire il flusso di controllo.


3
Dalla versione 2.3 Python utilizza la "risoluzione del metodo C3" spiegata in Ordine di risoluzione del metodo Python 2.3 o Ordine di risoluzione del metodo .
webwurst,

11
Personalmente, nella maggior parte dei casi prenderei i mixin sul patching delle scimmie; è più facile ragionare e seguire il codice.
tdammers,

5
Downvoted. Mentre la tua risposta esprime un'opinione valida sugli stili di sviluppo, in realtà non affronti la vera domanda.
Ryan B. Lynch,

8

Forse un paio di esempi aiuteranno.

Se stai costruendo una classe e vuoi che si comporti come un dizionario, puoi definire tutti i vari __ __metodi necessari. Ma è un po 'una seccatura. In alternativa, puoi semplicemente definirne alcuni ed ereditare (oltre a qualsiasi altra eredità) da UserDict.DictMixin(spostato in collections.DictMixinin py3k). Ciò avrà l'effetto di definire automaticamente tutto il resto dell'API del dizionario.

Un secondo esempio: il toolkit della GUI wxPython consente di creare controlli di elenco con più colonne (come, ad esempio, la visualizzazione dei file in Esplora risorse). Per impostazione predefinita, questi elenchi sono abbastanza semplici. Puoi aggiungere funzionalità aggiuntive, come la possibilità di ordinare l'elenco in base a una colonna specifica facendo clic sull'intestazione della colonna, ereditando da ListCtrl e aggiungendo i mixin appropriati.


8

Non è un esempio di Python ma nel linguaggio di programmazione D il termine mixinè usato per riferirsi a un costrutto usato allo stesso modo; aggiungendo un mucchio di cose a una classe.

In D (che a proposito non fa MI) questo viene fatto inserendo un modello (pensa a macro sintatticamente consapevoli e sicure e sarai vicino) in un ambito. Ciò consente a una singola riga di codice in una classe, struttura, funzione, modulo o altro di espandersi a qualsiasi numero di dichiarazioni.


2
Mixin è un termine generale, usato in D, Ruby, ecc. Secondo Wikipedia, hanno avuto origine nei sistemi lisp della vecchia scuola e sono stati documentati per la prima volta nel 1983: en.wikipedia.org/wiki/…
Lee B

7

OP ha affermato di non aver mai sentito parlare del mixin in C ++, forse perché sono chiamati Curiously Recurring Template Pattern (CRTP) in C ++. Inoltre, @Ciro Santilli ha affermato che il mixin è implementato tramite la classe base astratta in C ++. Mentre la classe base astratta può essere utilizzata per implementare il mixin, è un overkill in quanto la funzionalità della funzione virtuale in fase di esecuzione può essere ottenuta utilizzando il modello in fase di compilazione senza l'overhead della ricerca della tabella virtuale in fase di run-time.

Il modello CRTP è descritto in dettaglio qui

Ho convertito l'esempio di Python nella risposta di @Ciro Santilli in C ++ usando la classe di template di seguito:

    #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: aggiunto il costruttore protetto in ComparableMixin in modo che possa essere ereditato e non solo istanziato. Aggiornato l'esempio per mostrare come il costruttore protetto provocherà un errore di compilazione quando viene creato un oggetto ComparableMixin.


Mixins e CRTP non sono esattamente la stessa cosa in C ++.
ashrasmun,

6

Forse un esempio dal rubino può aiutare:

Puoi includere il mixin Comparablee definire una funzione "<=>(other)", il mixin fornisce tutte quelle funzioni:

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

Lo fa invocando <=>(other)e restituendo il risultato giusto.

"instance <=> other"restituisce 0 se entrambi gli oggetti sono uguali, meno di 0 se instanceè maggiore di othere più di 0 se otherè maggiore.


Ecco un post che fornisce un mixin simile per Python. Sebbene il suggerimento si stia definendo __lt__come base anziché come __cmp__, quest'ultimo è in realtà deprecato e scoraggiato da usare. A me sembra più semplice usare quel mixin invece di decoratori piuttosto complicati (parte di funzioni ) - anche se questo potrebbe essere in grado di reagire in modo più dinamico su quali confronti sono forniti ...
Tobias Kienzler,

6

mixin offre un modo per aggiungere funzionalità in una classe, cioè è possibile interagire con i metodi definiti in un modulo includendo il modulo all'interno della classe desiderata. Anche se ruby ​​non supporta l'ereditarietà multipla ma fornisce il mixin come alternativa per raggiungere questo obiettivo.

ecco un esempio che spiega come si ottiene l'ereditarietà multipla usando il 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

4
Qual è la differenza tra questa e l'eredità multipla in generale?
Ciro Santilli 13 冠状 病 六四 事件 法轮功

La differenza è che non sei in grado di creare istanze da moduli, ma se non vi è alcuna differenziazione tra classi generali e moduli, i mixin non sono una cosa esplicita ed è difficile capire dove sia una classe generale e dove sia un mixin
ka8725

Quindi in Ruby i mixin sono solo classi che non possono essere istanziate ma devono essere utilizzate per l'ereditarietà multipla?
Trilarion

6

Ho appena usato un mixin Python per implementare test unitari per mungitori Python. Normalmente, un mungitore parla con un MTA, rendendo difficile il test unitario. Il mix di test ignora i metodi che parlano all'MTA e crea invece un ambiente simulato guidato da casi di test.

Quindi, prendi un'applicazione milter non modificata, come spfmilter e mixin TestBase, in questo modo:

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)

Quindi, utilizzare TestMilter nei casi di test per l'applicazione 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='good@example.com')
  self.assertEqual(rc,Milter.CONTINUE)
  milter.close()

http://pymilter.cvs.sourceforge.net/viewvc/pymilter/pymilter/Milter/test.py?revision=1.6&view=markup


4

Penso che le risposte precedenti abbiano definito molto bene cosa sono i MixIn . Tuttavia, al fine di comprenderli meglio, potrebbe essere utile confrontare MixIns con classi e interfacce astratte dal punto di vista del codice / implementazione:

1. Classe astratta

  • Classe che deve contenere uno o più metodi astratti

  • La classe astratta può contenere stati (variabili di istanza) e metodi non astratti

2. Interfaccia

  • L'interfaccia contiene solo metodi astratti (nessun metodo non astratto e nessuno stato interno)

3. MixIns

  • I MixIn (come le interfacce) non contengono stato interno (variabili di istanza)
  • MixIns contiene uno o più metodi non astratti ( possono contenere metodi non astratti a differenza delle interfacce)

In Python, ad esempio, queste sono solo convenzioni, perché tutto quanto sopra è definito come classes. Tuttavia, la caratteristica comune di entrambe le Classi astratte, Interfacce e MixIn è che non dovrebbero esistere da soli, cioè non dovrebbero essere istanziati.


3

Ho letto che hai un background ac #. Quindi un buon punto di partenza potrebbe essere un'implementazione di mixin per .NET.

Potresti voler dare un'occhiata al progetto codeplex su http://remix.codeplex.com/

Guarda il link al Simposio su lang.net per avere una panoramica. C'è ancora molto da scoprire nella documentazione sulla pagina di codeplex.

saluti Stefan

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.