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 Set
e Iterator
possono 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