Python LanguageПримеси

Синтаксис

  • class ClassName ( MainClass , Mixin1 , Mixin2 , ...): # Используется для объявления класса с именем ClassName , основным (первым) классом MainClass и mixins Mixin1 , Mixin2 и т. д.
  • class ClassName ( Mixin1 , MainClass , Mixin2 , ...): # «Основной» класс не должен быть первым классом; нет никакой разницы между ним и mixin

замечания

Добавление mixin в класс очень похоже на добавление суперкласса, потому что это в значительной степени именно это. Объект класса с mixin Foo также будет экземпляром Foo , а isinstance(instance, Foo) вернет true

Mixin

Mixin - это набор свойств и методов, которые могут использоваться в разных классах, которые не относятся к базовому классу. В языках объектно-ориентированного программирования вы обычно используете наследование для предоставления одинаковым функциям объектов разных классов; если набор объектов обладает некоторой способностью, вы помещаете эту способность в базовый класс, на который оба объекта наследуются .

Например, скажем, у вас есть классы Car , Boat и Plane . Объекты из всех этих классов имеют возможность путешествовать, поэтому они получают функцию travel . В этом сценарии все они перемещаются одним и тем же основным способом; путем получения маршрута и перемещения по нему. Чтобы реализовать эту функцию, вы можете получить все классы из Vehicle и поместить функцию в этот общий класс:

class Vehicle(object):
   """A generic vehicle class."""

   def __init__(self, position):
       self.position = position

   def travel(self, destination):
       route = calculate_route(from=self.position, to=destination)
       self.move_along(route)

class Car(Vehicle):
   ...

class Boat(Vehicle):
   ...

class Plane(Vehicle):
   ...

С помощью этого кода вы можете позвонить в travel по машине ( car.travel("Montana") ), лодку ( boat.travel("Hawaii") ) и самолет ( plane.travel("France") )

Однако, что, если у вас есть функциональность, недоступная базовому классу? Скажем, например, вы хотите дать Car радио и возможность использовать его для воспроизведения песни на радиостанции с помощью play_song_on_station , но у вас также есть Clock которые также могут использовать радио. Car и Clock могут делиться базовым классом ( Machine ). Однако не все машины могут воспроизводить песни; Boat и Plane не могут (по крайней мере, в этом примере). Итак, как вы это делаете без дублирования кода? Вы можете использовать mixin. В Python предоставление класса mixin так же просто, как добавление его в список подклассов, например

class Foo(main_super, mixin): ...

Foo наследует все свойства и методы main_super , а также свойства mixin .

Итак, чтобы дать классу Car и часы возможность использовать радио, вы можете переопределить Car из последнего примера и написать это:

class RadioUserMixin(object):
   def __init__(self):
       self.radio = Radio()

   def play_song_on_station(self, station):
       self.radio.set_station(station)
       self.radio.play_song()

class Car(Vehicle, RadioUserMixin):
   ...

class Clock(Vehicle, RadioUserMixin):
   ...

Теперь вы можете вызвать car.play_song_on_station(98.7) и clock.play_song_on_station(101.3) , но не что-то вроде boat.play_song_on_station(100.5)

Важная вещь с mixins заключается в том, что они позволяют добавлять функциональные возможности к различным объектам, которые не разделяют «основной» подкласс с этой функциональностью, но тем не менее все равно используют код для него. Без миксинов делать что-то вроде приведенного выше примера было бы намного сложнее и / или потребовало бы повторения.

Переопределение методов в миксинах

Mixins - это своего рода класс, который используется для «смешения» дополнительных свойств и методов в классе. Это обычно прекрасно, потому что много раз классы mixin не переопределяют друг друга или методы базового класса. Но если вы переопределяете методы или свойства в ваших микшинах, это может привести к неожиданным результатам, поскольку в Python иерархия классов определяется справа налево.

Например, возьмите следующие классы

class Mixin1(object):
    def test(self):
        print "Mixin1"

class Mixin2(object):
    def test(self):
        print "Mixin2"

class BaseClass(object):
    def test(self):
        print "Base"

class MyClass(BaseClass, Mixin1, Mixin2):
    pass

В этом случае класс Mixin2 является базовым классом, расширенным Mixin1 и, наконец, BaseClass. Таким образом, если мы выполним следующий фрагмент кода:

>>> x = MyClass()
>>> x.test()
Base

Мы видим, что результат возвращается из класса Base. Это может привести к непредвиденным ошибкам в логике вашего кода, и их необходимо учитывать и учитывать