Миксин
Миксин (дословно: «примесь) — класс (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 — Использование миксинов
Для сокращения кода в 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); > > > >
С таким подходом можно на лету подменять методы в примеси, и эти изменения сразу будут доступны для всех объектов эту примесь использующих.
Очень-очень маленьким недостатком является то, что не слишком умная подсказка в среде разработки будет всегда показывать что у методов нет аргументов.
Весь код и примеры лежат себе спокойно вот тут