Вопрос-Ответ

What is a mixin and why is it useful?

Что такое микширование и почему оно полезно?

В книге "Программирование на Python" Марк Латц упоминает термин "миксин". Я работаю на C / C ++ / C # и раньше не слышал этого термина. Что такое микширование?

Читая между строк этого примера (на который я дал ссылку, потому что он довольно длинный), я предполагаю, что это случай использования множественного наследования для расширения класса в отличие от правильного создания подклассов. Правильно ли это?

Почему я должен хотеть сделать это, а не поместить новую функциональность в подкласс? Если уж на то пошло, почему подход микширования / множественного наследования лучше, чем использование композиции?

Что отличает микширование от множественного наследования? Это просто вопрос семантики?

Переведено автоматически
Ответ 1

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


  1. Вы хотите предоставить множество дополнительных функций для класса.

  2. Вы хотите использовать одну конкретную функцию во множестве разных классов.

В качестве примера номер один рассмотрим систему запросов и ответов werkzeug. Я могу создать простой старый объект запроса, сказав:

from werkzeug import BaseRequest

class Request(BaseRequest):
pass

Если я хочу добавить поддержку заголовков accept, я бы сделал это

from werkzeug import BaseRequest, AcceptMixin

class Request(AcceptMixin, BaseRequest):
pass

Если бы я хотел создать объект запроса, поддерживающий заголовки accept, etags, аутентификацию и поддержку пользовательского агента, я мог бы сделать это:

from werkzeug import BaseRequest, AcceptMixin, ETagRequestMixin, UserAgentMixin, AuthenticationMixin

class Request(AcceptMixin, ETagRequestMixin, UserAgentMixin, AuthenticationMixin, BaseRequest):
pass

Разница неуловима, но в приведенных выше примерах классы mixin не создавались сами по себе. В более традиционном множественном наследовании AuthenticationMixin (например), вероятно, было бы чем-то более похожим на Authenticator. То есть класс, вероятно, был бы разработан так, чтобы работать сам по себе.

Ответ 2

Во-первых, вы должны отметить, что микширования существуют только в языках с множественным наследованием. Вы не можете выполнить микширование в Java или C #.

По сути, mixin - это автономный базовый тип, который обеспечивает ограниченную функциональность и полиморфный резонанс для дочернего класса. Если вы думаете на C #, подумайте об интерфейсе, который вам не нужно реализовывать на самом деле, потому что он уже реализован; вы просто наследуете его и извлекаете выгоду из его функциональности.

Миксины обычно имеют узкую область применения и не предназначены для расширения.

[редактировать -- относительно того, почему:]

Полагаю, я должен объяснить, почему, раз уж вы спросили. Большим преимуществом является то, что вам не нужно делать это самостоятельно снова и снова. В C # наибольшее преимущество микширования может принести шаблон удаления. Всякий раз, когда вы реализуете IDisposable, вы почти всегда хотите следовать одному и тому же шаблону, но в конечном итоге вы пишете и переписываете один и тот же базовый код с незначительными изменениями. Если бы существовал расширяемый утилизационный миксин, вы могли бы сэкономить массу времени на наборе текста.

[правка 2 - для ответа на другие ваши вопросы]


Что отличает микширование от множественного наследования? Это просто вопрос семантики?


ДА. Разница между микшированием и стандартным множественным наследованием заключается всего лишь в семантике; класс с множественным наследованием может использовать микширование как часть этого множественного наследования.

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

Опять же, подумайте об интерфейсе, который уже реализован.

Лично я не использую миксины, поскольку разрабатываю в основном на языке, который их не поддерживает, поэтому мне действительно сложно придумать достойный пример, который просто обеспечит вам этот момент "ахах!". Но я попробую еще раз. Я собираюсь использовать надуманный пример - большинство языков тем или иным образом уже предоставляют эту функцию - но это, надеюсь, объяснит, как миксины должны создаваться и использоваться. Далее:

Предположим, у вас есть тип, который вы хотите иметь возможность сериализовывать в XML и из XML. Вы хотите, чтобы тип предоставлял метод "ToXML", который возвращает строку, содержащую XML-фрагмент со значениями данных типа, и "FromXML", который позволяет типу восстанавливать свои значения данных из XML-фрагмента в строке. Опять же, это надуманный пример, поэтому, возможно, вы используете файловый поток или класс XML Writer из библиотеки времени выполнения вашего языка... неважно. Дело в том, что вы хотите сериализовать свой объект в XML и получить обратно новый объект из XML.

Другим важным моментом в этом примере является то, что вы хотите сделать это универсальным способом. Вы не хотите реализовывать методы "ToXML" и "FromXML" для каждого типа, который вы хотите сериализовать, вам нужны какие-то универсальные средства, гарантирующие, что ваш тип будет это делать, и это просто работает. Вы хотите повторно использовать код.

Если ваш язык поддерживает его, вы могли бы создать XmlSerializable mixin, который выполнит вашу работу за вас. Этот тип будет реализовывать методы ToXML и FromXML . Он мог бы, используя какой-то механизм, который не важен для примера, собирать все необходимые данные из любого типа, с которым он смешан, для построения фрагмента XML, возвращаемого ToXML, и он был бы в равной степени способен восстанавливать эти данные при вызове FromXML .

И .. вот и все. Чтобы использовать его, у вас должен быть любой тип, который необходимо сериализовать в XML, наследуемый от XmlSerializable . Всякий раз, когда вам нужно сериализовать или десериализовать этот тип, вы просто вызываете ToXML или FromXML . Фактически, поскольку XmlSerializable является полноценным типом и полиморфным, вы могли бы создать сериализатор документов, который ничего не знает о вашем исходном типе, принимая только, скажем, массив XmlSerializable типов.

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

Если вы просто думаете о mixin как о небольшом базовом типе, предназначенном для добавления небольшого количества функциональности к типу, никак не влияя на этот тип, то вы молодец.

Надеюсь. :)

Ответ 3

This answer aims to explain mixins with examples that are:


  • self-contained: short, with no need to know any libraries to understand the example.



  • in Python, not in other languages.


    It is understandable that there were examples from other languages such as Ruby since the term is much more common in those languages, but this is a Python thread.



It shall also consider the controversial question:


Is multiple inheritance necessary or not to characterize a mixin?


Definitions

I have yet to see a citation from an "authoritative" source clearly saying what is a mixin in Python.

I have seen 2 possible definitions of a mixin (if they are to be considered as different from other similar concepts such as abstract base classes), and people don't entirely agree on which one is correct.

The consensus may vary between different languages.

Definition 1: no multiple inheritance

A mixin is a class such that some method of the class uses a method which is not defined in the class.

Therefore the class is not meant to be instantiated, but rather serve as a base class. Otherwise the instance would have methods that cannot be called without raising an exception.

A constraint which some sources add is that the class may not contain data, only methods, but I don't see why this is necessary. In practice however, many useful mixins don't have any data, and base classes without data are simpler to use.

A classic example is the implementation of all comparison operators from only <= and ==:

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

This particular example could have been achieved via the functools.total_ordering() decorator, but the game here was to reinvent the wheel:

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)

Definition 2: multiple inheritance

A mixin is a design pattern in which some method of a base class uses a method it does not define, and that method is meant to be implemented by another base class, not by the derived like in Definition 1.

The term mixin class refers to base classes which are intended to be used in that design pattern (TODO those that use the method, or those that implement it?)

It is not easy to decide if a given class is a mixin or not: the method could be just implemented on the derived class, in which case we're back to Definition 1. You have to consider the author's intentions.

This pattern is interesting because it is possible to recombine functionalities with different choices of base classes:

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

Авторитетные вхождения Python

В официальной документации для collections.abc в документации явно используется термин Методы микширования.

В нем говорится, что если класс:


  • реализует __next__

  • наследуется от одного класса Iterator

затем класс получает __iter__ метод mixin бесплатно.

Следовательно, по крайней мере, в этом пункте документации, mixin не требует множественного наследования и согласуется с Определением 1.

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

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

На других языках


  • Ruby: Явно не требует множественного наследования для mixin, как упоминается в основных справочниках, таких как Programming Ruby и язык программирования Ruby



  • C ++: virtual Метод, который установлен =0, является чисто виртуальным методом.


    Определение 1 совпадает с определением абстрактного класса (класса, который имеет чисто виртуальный метод). Этот класс не может быть создан.


    Определение 2 возможно при виртуальном наследовании: Множественное наследование от двух производных классов



Ответ 4

Я думаю о них как о дисциплинированном способе использования множественного наследования - потому что, в конечном счете, mixin - это просто еще один класс python, который (может) следовать соглашениям о классах, называемых mixins.

Мое понимание соглашений, которые регулируют то, что вы назвали бы микшированием, заключается в том, что микширование:


  • добавляет методы, но не переменные экземпляра (константы класса в порядке)

  • наследуется только от object (в Python)

Таким образом, он ограничивает потенциальную сложность множественного наследования и позволяет достаточно легко отслеживать ход выполнения вашей программы, ограничивая, куда вам нужно смотреть (по сравнению с полным множественным наследованием). Они похожи на модули ruby.

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

Сказав это, я видел классы под названием XYZMixin, которые действительно имеют переменные экземпляра.

python