Что такое mixin
Перейти к содержимому

Что такое mixin

  • автор:

Миксин

Миксин (дословно: «примесь) — класс (class) или интерфейс, в котором некоторые или все его методы (methods) и/или свойства (properties) не реализованы, требуя, чтобы другой класс или интерфейс обеспечивал недостающие реализации. Новый класс или интерфейс затем включает в себя как свойства и методы из миксина, так и те, которые он определяет сам. Все методы и свойства используются совершенно одинаково, независимо от того, реализованы ли они в миксине, интерфейсе или классе, реализующем миксин.

Преимущество миксинов заключается в том, что они могут быть использованы для упрощения проектирования API, в которых несколько интерфейсов должны включать одни и те же методы и свойства.

Например, миксин WindowOrWorkerGlobalScope используется для предоставления методов и свойств, которые должны быть доступны как в интерфейсах Window , так и в интерфейсах WorkerGlobalScope (en-US). Миксин осуществляется с помощью обоих этих интерфейсов.

Узнать больше

General knowledge

  • Mixin on Wikipedia

Found a content problem with this page?

  • Edit the page on GitHub.
  • Report the content issue.
  • View the source on GitHub.

This page was last modified on 3 авг. 2023 г. by MDN contributors.

Your blueprint for a better internet.

Что такое миксины в Python и как их использовать

Mixin классы — это концепция в программировании, в которой класс предоставляет функциональные возможности, но не предназначен для самостоятельного использования. Основная цель миксинов — предоставить какие-то дополнительные методы.

Другими словами классы миксины или как еще их называют примеси — это ограниченная форма множественного наследования. В частности, в контексте языка Python, миксин — это родительский класс, который предоставляет функциональные возможности подклассам, но не предназначен для создания экземпляров самого себя. И было бы лучше, если бы сами миксины не имели наследования от других миксинов, а также избегали какого либо состояния.

В Python нет какого либо специального синтаксиса для поддержки миксинов, по этому классы миксинов, легко можно перепутать с обычными классами, но при этом у них действительно очень большая как семантическая, так и реальная разница с обычными классами.

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

Пример множественного наследования.

# test.py from collections import Counter, OrderedDict class OrderedCounter(Counter, OrderedDict): """Счетчик, который запоминает порядок, в котором элементы встречаются впервые""" def __repr__(self): return f'self.__class__.__name__>(OrderedDict(self)!r>)' def __reduce__(self): return self.__class__, (OrderedDict(self),) # запускаем # $ python3 -i test.py >>> od = OrderedCounter() >>> repr(od) # 'OrderedCounter(OrderedDict())' 

Он создает подкласс Counter и OrderedDict , которые импортируются из модуля collections .

И Counter , и OrderedDict предназначены для самостоятельного использования в качестве экземпляров. Создав подкласс из обоих классов, получаем счетчик, который будет упорядочен и повторно использует код в каждом объекте. Это мощный способ повторного использования кода, но он также может быть проблематичным. Так как, если выяснится, что в одном из объектов есть ошибка, то ее исправление может создать ошибку в подклассе.

Пример использования класса миксина/примеси.

Миксины обычно продвигаются как способ повторного использования кода без потенциальных проблем связанности, которые могут возникнуть при кооперативном множественном наследовании, таком как в OrderedCounter() . Когда используются миксины, то по сути используется функциональность, которая не так тесно связана с данными.

В отличие от приведенного выше примера, класс миксина не предназначен для использования отдельно. Он предоставляет новые методы или переопределяет имеющиеся методы.

Например, в стандартной библиотеке Python, в модуле socketserver есть несколько миксинов. Выдержка из документации:

С помощью этих классов миксинов могут быть созданы поточные версии каждого типа сервера. Например, ThreadingUDPServer создается следующим образом:

class ThreadingUDPServer(ThreadingMixIn, UDPServer): pass 

Первым, идет класс миксина ThreadingMixIn , так как он переопределяет метод process_request() и server_close() , определенный в классе UDPServer() , к тому же, добавляет новую функциональность, чтобы обеспечить параллелизм, а именно — новый метод process_request_thread() .

Простой пример для понимания поведения классов миксинов.

Классы миксинов в Python — это действительно отличная концепция, которая позволяет создавать классы в композиционном стиле.

# test.py class Entity: def __init__(self, pos_x, pos_y): self.pos_x = pos_x self.pos_y = pos_y class SquareMixin: def add_size(self, size_x): self.size_x = size_x self.size_y = size_x def perimeter(self): return self.size_x * 4 def square(self): return self.size_x * self.size_x class SquareEntity(SquareMixin, Entity): pass # запускаем # $ python3 -i test.py >>> square = SquareEntity(5, 4) >>> square.add_size(500) >>> square.size_x # 500 >>> square.size_y # 500 >>> square.square() # 250000 >>> square.perimeter() # 2000 

Здесь итоговый класс SquareEntity() получает от класса миксина SquareMixin() методы добавления размера квадрата, а так же вычисления его периметра и площади. Данное поведение упрощает дерево наследования SquareEntity() , что позволяет использовать класс Entity() в качестве родителя для других фигур без необходимости наследовать методы, которые не нужны (например для круга).

Иногда молодые программисты не до конца понимают принцип MRO в Python и по этому в некоторых случаях не правильно используют классы миксинов.

# test.py class BaseClass: def test(self): print('BaseClass') class Mixin: def test(self): print('Mixin') class MyClass(BaseClass, Mixin): pass # запускаем # $ python3 -i test.py >>> obj = MyClass() # класс миксина не работает >>> obj.test() # BaseClass 

В Python иерархия классов определяется справа налево, поэтому в этом случае класс Mixin() является базовым классом и расширяется BaseClass() . Обычно это нормально, потому что во многих случаях классы миксинов не переопределяют методы друг друга или базового класса. Но если в миксинах идет переопределение метода или свойства, то это может привести к неожиданным результатам, т. к. приоритет разрешения методов — слева направо.

class MyClass(Mixin, BaseClass): pass >>> obj = MyClass() # теперь все нормально >>> obj.test() # Mixin 

Использование функции super() в миксинах.

Например, если стоит необходимость явной передачи параметра size при инициализации класса SquareEntity() из примера выше, то необходимо воспользоваться функцией super() .

# test.py class Entity: def __init__(self, pos_x, pos_y): self.pos_x = pos_x self.pos_y = pos_y class SquareMixin: def __init__(self, size, **kwargs): super().__init__(**kwargs) self.size_x = size self.size_y = size def perimeter(self): return self.size_x * 4 def square(self): return self.size_x * self.size_x class SquareEntity(SquareMixin, Entity): def __init__(self, **kwargs): super().__init__(**kwargs) # все аргументы функции работают # исключительно как ключевые square = SquareEntity(pos_x=5, pos_y=4, size=500) # запускаем # $ python3 -i test.py >>> square.size_x # 500 >>> square.size_y # 500 >>> square.perimeter() # 2000 >>> square.square() # 250000 
  • ОБЗОРНАЯ СТРАНИЦА РАЗДЕЛА
  • Пространство имен и область видимости в классах
  • Определение классов
  • Объект класса и конструктор класса
  • Создание экземпляра класса
  • Метод экземпляра класса
  • Что такое метод класса и зачем нужен
  • Что такое статический метод в классах Python и зачем нужен
  • Атрибуты класса и переменные экземпляра класса
  • Кэширование методов экземпляра декоратором lru_cache
  • Закрытые/приватные методы и переменные класса Python
  • Наследование классов
  • Множественное наследование классов
  • Абстрактные классы
  • Перегрузка методов в классе Python
  • Что такое миксины и как их использовать
  • Класс Python как структура данных, подобная языку C
  • Создание пользовательских типов данных
  • Специальные (магические) методы класса Python
  • Базовая настройка классов Python магическими методами
  • Настройка доступа к атрибутам класса Python
  • Дескриптор класса для чайников
  • Протокол дескриптора класса
  • Практический пример дескриптора
  • Использование метода .__new__() в классах Python
  • Специальный атрибут __slots__ класса Python
  • Специальный метод __init_subclass__ класса Python
  • Определение метаклассов metaclass
  • Эмуляция контейнерных типов в классах Python
  • Другие специальные методы класса
  • Как Python ищет специальные методы в классах
  • Шаблон проектирования Фабрика и его реализация

Урок #4 — Использование миксинов

Урок #4 - Использование миксинов

Для сокращения кода в SASS используются специальные шаблоны со свойствами — миксины. В ходе урока вы научитесь создавать миксины, передавать в них параметры, а также использовать их для различных объектов.

Видеоурок

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

Для создания миксина используйте ключевое слово @mixin :

@mixin someName < // Здесь код и различные стили >

Миксины не срабатывают без явного подключения, поэтому сейчас в файле CSS ничего не поменяется. Для подключения миксина используйте ключевое слово @include .

#block

Также в миксины можно передавать параметры. Для этого после имени миксина пропишите круглые скобки ( ) , после чего в них пропишите параметры, которые будут переданы в него.

Javascript примеси для чайников

После того, как с классическим (от слова класс) наследованием в JS стало вроде-бы все понятно, я решил разобраться с реализацией другого способа повторного использвоания кода — примесями. Несмотря на довольно непривычное название, способ этот чертовски прост.

1. Примеси — это, собственно, что ?

Примесь (mixin) — это объект с набором функций, который сам по себе (отдельно от других объектов) не используется.

Вот, например, прекрасная примесь:

var Mixin_Babbler = < say: function () < console.log("My name is " + this.name + " and i think:'" + this.THOUGHTS + "'"); >, argue: function() < console.log("You're totally wrong"); >>; 

При попытке вызвать метод say просто так, нас ждет облом, потому что ни this.name, ни this.THOUGHTS в нашем объекте, почему-то, просто нет.
На самом деле все правильно, чтобы использовать примесь по назначению нам нужен другой объект, и метод, который копирует все свойства переданных ему объектов-примесей в прототип функции конструктора — обычно такой метод называют extend, или mix, или как-нибудь в этом духе:

function extend(object) < var mixins = Array.prototype.slice.call(arguments, 1); for (var i = 0; i < mixins.length; ++i) < for (var prop in mixins[i]) < if (typeof object.prototype[prop] === "undefined") < object.prototype[prop] = mixins[i][prop]; >> > > 
2. Ну и как это использовать?

Легко и не напрягаясь — допустим, у нас есть парочка примесей:

var Mixin_Babbler = < say: function () < console.log("My name is " + this.name + " and i think:'" + this.THOUGHTS + "'"); >, argue: function() < console.log("You're totally wrong"); >>; var Mixin_BeverageLover = < drink: function () < console.log("* drinking " + this.FAVORITE_BEVERAGE + " *"); >>; 

И, кому-то, возможно, уже знакомая, эволюционная цепочка:

function Man(name) < this.name = name; >Man.prototype = < constructor: Man, THOUGHTS: "I like soccer" >extend(Man, Mixin_Babbler); function Gentleman(name) < this.name = name; >Gentleman.prototype = < constructor: Gentleman, THOUGHTS: "I like Earl Grey", FAVORITE_BEVERAGE: "Tea" >extend(Gentleman, Mixin_Babbler, Mixin_BeverageLover); function Programmer(name) < this.name = name; >Programmer.prototype = < constructor: Programmer, THOUGHTS: "MVC, MVVM, MVP *___* like it!", FAVORITE_BEVERAGE: "Cofee", write_good_code: function () < console.log("*writing best code ever*"); this.drink(); >> extend(Programmer, Mixin_Babbler, Mixin_BeverageLover); 

Каждый «класс» теперь не зависит от другого, а весь повторяющийся функционал реализован с помощью примесей.

Теперь стоит все проверить — вдруг заработает:

var man = new Man("Bob"); var gentleman = new Gentleman("Bill"); var programmer = new Programmer("Benjamin"); man.say(); man.argue(); gentleman.say(); gentleman.drink(); programmer.say(); programmer.write_good_code(); 

И консоль говорит что таки да, все как надо:

My name is Bob and i think:'I like soccer' *You're totally wrong* My name is Bill and i think:'I like Earl Grey' *drinking Tea* My name is Benjamin and i think:'MVC, MVVM, MVP like *__* it!' *writing best code ever* *drinking Cofee* 

Собственно все. В отличие от «классического» наследования, реализация примесей очень простая и понятная. Существуют конечно некоторые вариации, но так или иначе ядро выглядит именно так.

UPD. Как подсказал один очень хороший человек с ником lalaki, метод extend можно реализовать немножко по-другому:

function extend_2(object) < var mixins = Array.prototype.slice.call(arguments, 1); for (var i = 0; i < mixins.length; ++i) < for (var prop in mixins[i]) < if (typeof object.prototype[prop] === "undefined") < bindMethod = function (mixin, prop) < return function () < mixin[prop].apply(this, arguments) >> object.prototype[prop] = bindMethod(mixins[i], prop); > > > > 

С таким подходом можно на лету подменять методы в примеси, и эти изменения сразу будут доступны для всех объектов эту примесь использующих.
Очень-очень маленьким недостатком является то, что не слишком умная подсказка в среде разработки будет всегда показывать что у методов нет аргументов.

Весь код и примеры лежат себе спокойно вот тут

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *