Ta odpowiedź ma na celu wyjaśnienie mixin przykładami , które:
samodzielny : krótki, bez potrzeby znajomości bibliotek, aby zrozumieć przykład.
w Pythonie , a nie w innych językach.
Zrozumiałe jest, że istniały przykłady z innych języków, takich jak Ruby, ponieważ termin jest znacznie bardziej powszechny w tych językach, ale jest to wątek w języku Python .
Rozważy również kontrowersyjne pytanie:
Czy konieczne jest wielokrotne dziedziczenie, czy nie charakterystyka miksu?
Definicje
Nie widziałem jeszcze cytatu z „autorytatywnego” źródła, w którym wyraźnie mówi się, co to jest mixin w Pythonie.
Widziałem 2 możliwe definicje mixinu (jeśli należy je uważać za różne od innych podobnych pojęć, takich jak abstrakcyjne klasy podstawowe), a ludzie nie do końca zgadzają się co do tego, która z nich jest poprawna.
Konsensus może być różny w różnych językach.
Definicja 1: brak wielokrotnego dziedziczenia
Mixin jest taką klasą, że niektóre metody tej klasy wykorzystują metodę, która nie jest zdefiniowana w klasie.
Dlatego klasa nie jest przeznaczona do tworzenia instancji, ale raczej służy jako klasa podstawowa. W przeciwnym razie instancja miałaby metody, których nie można wywołać bez zgłaszania wyjątku.
Ograniczeniem dodawanym przez niektóre źródła jest to, że klasa może nie zawierać danych, tylko metody, ale nie rozumiem, dlaczego jest to konieczne. W praktyce jednak wiele przydatnych miksów nie ma żadnych danych, a klasy podstawowe bez danych są prostsze w użyciu.
Klasycznym przykładem jest implementacja wszystkich operatorów porównania tylko <=
i ==
:
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
Ten konkretny przykład można było osiągnąć za pomocą functools.total_ordering()
dekoratora, ale tutaj gra miała na celu wynalezienie koła:
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)
Definicja 2: wielokrotne dziedziczenie
Mixin to wzorzec projektowy, w którym jakaś metoda klasy bazowej wykorzystuje metodę, której nie definiuje, i ta metoda ma być zaimplementowana przez inną klasę bazową , a nie przez pochodną jak w definicji 1.
Termin klasa mixin odnosi się do klas podstawowych, które mają być stosowane w tym wzorze projektowym (TODO tych, które stosują tę metodę, lub tych, które ją wdrażają?)
Nie jest łatwo zdecydować, czy dana klasa jest miksem, czy nie: metoda może być po prostu zaimplementowana w klasie pochodnej, w którym to przypadku wracamy do definicji 1. Musisz wziąć pod uwagę intencje autora.
Ten wzór jest interesujący, ponieważ możliwe jest ponowne połączenie funkcjonalności z różnymi wyborami klas podstawowych:
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
Autorytatywne wystąpienia Pythona
W oficjalnej dokumentacji dla kolekcji.abc w dokumentacji wyraźnie użyto terminu Metody mieszania .
Stwierdza, że jeśli klasa:
- narzędzia
__next__
- dziedziczy po jednej klasie
Iterator
wtedy klasa otrzymuje __iter__
metodę miksowania za darmo.
Dlatego przynajmniej w tym punkcie dokumentacji mixin nie wymaga wielokrotnego dziedziczenia i jest spójny z definicją 1.
Dokumentacja może oczywiście być sprzeczna w różnych punktach, a inne ważne biblioteki Pythona mogą używać innej definicji w swojej dokumentacji.
Ta strona używa również terminu Set mixin
, który wyraźnie sugeruje, że klasy lubią Set
i Iterator
mogą być nazywane klasami Mixin.
W innych językach
Ruby: Najwyraźniej nie wymaga wielokrotnego dziedziczenia dla mixin, jak wspomniano w głównych książkach, takich jak Programowanie Ruby i Język programowania Ruby
C ++: Metoda, która nie jest zaimplementowana, jest metodą czysto wirtualną.
Definicja 1 pokrywa się z definicją klasy abstrakcyjnej (klasy, która ma czysto wirtualną metodę). Nie można utworzyć tej klasy.
Definicja 2 jest możliwa w przypadku dziedziczenia wirtualnego: wielokrotne dziedziczenie z dwóch klas pochodnych