Python поддерживает простой тип множественного наследования, который позволяет создавать Mixin-ы. Mixin-ы или примеси являются своего рода классом, который используется для «подмешивания» дополнительных свойств и методов в класс. Это позволяет создавать классы в композиционном стиле.
Mixin-ы — действительно отличная концепция, но я часто обнаруживаю, что люди используют их неправильно, что может привести к некоторым ошибкам. Я часто вижу Mixins, которые используется так:
1 2 |
class MyClass(BaseClass, Mixin1, Mixin2): pass |
Ожидая при этом, что порядок применения Mixin-ов будет соответствовать указанному — слева направо.
Однако, в Python иерархия классов определяется в порядке не слева направо, а справа налево, так что в этом случае класс Mixin2 является базовым классом, расширенный Mixin1 и, наконец, BaseClass. Если учитывать это обстоятельство, то вы можете правильно читать иерархии классов (MyClass => Mixin2 => Mixin1 => BaseClass) и сможете избежать многих ошибок.
Пример, который наглядно показывает порядок применения Mixin-ов:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
class BaseClass(object): def test(self): return 'BaseClass' class Mixin1(object): def test(self): return 'Mixin1' class Mixin2(object): def test(self): return 'Mixin2' class MyClass1(BaseClass, Mixin1, Mixin2): pass class MyClass2(Mixin2, Mixin1, BaseClass): pass print MyClass1().test() # BaseClass print MyClass2().test() # Mixin2 |
Особенность обхода классов во время поиска метода заключается еще в том, что сначала метод ищется в самом классе MainClass, потом в его родителях, и только потом в родительском классе родителей. Как показано на примере ниже, порядок обхода классов во время поиска метода test() будет следующий: MainClass -> Class1 -> Class2 -> Mixin1
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
class A(object): def foo(self): prin("class A") class Mixin1(object): def test(self): return 'Mixin1' class Class1(Mixin1): pass class Class2(Mixin1): def test(self): return 'Class2' class MainClass(Class1,Class2): pass print MainClass().test() # Class2 |
Таким образом результат при выполнении метода MainClass().test() будет таким: Class2