Являются ли Python mixins анти-паттерном?

34

Я полностью осознаю, что pylintи другие инструменты статического анализа не являются всезнающими, и иногда их советам нужно не подчиняться. (Это относится к различным классам сообщений, а не только к convention.)


Если у меня есть классы, такие как

class related_methods():

    def a_method(self):
        self.stack.function(self.my_var)

class more_methods():

    def b_method(self):
        self.otherfunc()

class implement_methods(related_methods, more_methods):

    def __init__(self):
        self.stack  = some()
        self.my_var = other()

    def otherfunc(self):
        self.a_method()

Очевидно, что это надумано. Вот лучший пример, если хотите .

Я считаю, что этот стиль называется использованием "mixins".

Как и другие инструменты, pylintоценивает этот код -21.67 / 10, в первую очередь потому, что он думает, more_methodsа related_methodsне имеет selfили имеет атрибуты otherfunc, stackи my_varпотому , что без запуска кода он, очевидно, не может видеть related_methodsи more_methodsсмешиваться с ним implement_methods.

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

Почему инструменты статического анализа отклоняют этот допустимый (я думаю) шаблон ООП?

Или:

  1. Они даже не пытаются проверить наследство или

  2. миксин не рекомендуется в идиоматическом, читаемом Python


№ 1, очевидно, неверно, потому что, если я попрошу pylintрассказать мне о моем классе, который наследует, unittest.TestCaseкоторый использует self.assertEqual(что-то определено только в unittest.TestCase), он не будет жаловаться.

Миксины не пифоничны или не одобряются?

Кот
источник
1
Это плохой вопрос? Это не по теме? это неопровержимый? Я тупой? Люди могут DV, но они не хотят помочь улучшить это.
кот
1
Есть некоторые, которые, вероятно, устали от того, чтобы люди спрашивали «это (анти) шаблон» или «это (не) питон».
9
@MichaelT Это нормально. Тогда они могут перестать рассматривать вопросы.
Katana314
2
@tac люди будут понижать голос по разным причинам, но никогда не обязаны говорить, почему - кнопка понижающего голоса имеет всплывающую подсказку, которая гласит: «Этот вопрос не требует каких-либо исследовательских усилий; он неясен или бесполезен» приемлемый ответ. 8 вверх и 1 вниз на самом деле довольно хорошо, особенно по вопросу, где отрицательные голоса бесплатны. Не позволяйте этому сбить вас с толку. Приветствия.
Аарон Холл
@AaronHall Я знаю, что людям разрешено делать то, что им нравится, с их голосами, действительно, я говорю новым пользователям, которые жалуются на то же самое.
кот

Ответы:

16

Mixins просто не тот случай использования, который рассматривался инструментом. Это не означает, что это обязательно плохой вариант использования, просто необычный для Python.

Правильно ли используются миксины в конкретном случае - это другой вопрос. Мишиновый анти-паттерн, который я вижу чаще всего, использует миксины, когда предполагается, что это только одна комбинация. Это просто окольный способ скрыть класс бога. Если вы не можете думать о причине прямо сейчас , чтобы поменять или оставить одну из Mixins, она не должна быть Mixin.

Карл Билефельдт
источник
да, это божественный объект, я признаю. В этом случае я бы поспорил, что это нормально для процессора, чтобы быть божественным объектом.
кот
15

Я считаю, что Миксинс, безусловно, может быть Pythonic. Тем не менее, идиоматический способ заставить замолчать ваш линтер - и улучшить читаемость вашего Mixins - это как (1) определять абстрактные методы, которые явно определяют методы, которые должны реализовывать потомки Mixin, так и (2) предварительно определять Noneполя для элементов данных Mixin, которые должны инициализировать дочерние элементы.

Применение этого шаблона к вашему примеру:

from abc import ABC, abstractmethod


class related_methods():
    my_var = None
    stack = None

    def a_method(self):
        self.stack.function(self.my_var)


class more_methods(ABC):
    @abstractmethod
    def otherfunc(self):
        pass

    def b_method(self):
        self.otherfunc()


class implement_methods(related_methods, more_methods):
    def __init__(self):
        self.stack  = some()
        self.my_var = other()

    def otherfunc(self):
        self.a_method()
UltraBird
источник
2
+1 за ваш ответ, хотя abstract other(self): passвещь
кошка
Это еще более запутанно без @abstractmethod ;-) Я сначала начал с Java, так что это просто отлично для меня.
Крис Хуан-Ливер
9

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

Хороший класс, включая миксин, несет одну четкую ответственность. В идеале миксин должен нести все состояние, к которому он собирается получить доступ, и логику для его обработки. Например, хороший миксин может добавить last_updatedполе в класс модели ORM, предоставить логику для его установки и методы для поиска самой старой / самой новой записи.

Ссылка на необъявленные члены экземпляра (переменные и методы) выглядит немного странно.

Правильный подход во многом зависит от поставленной задачи.

Это может быть миксин с соответствующим битом состояния, хранящимся в нем.

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

Это может быть декоратор класса, который добавляет метод или два; обычно это имеет смысл, когда нужно передать некоторые аргументы декоратору, чтобы повлиять на генерацию метода.

Изложите свою проблему, объясните свои большие проблемы с дизайном, и мы могли бы поспорить, если в вашем случае что-то противоречит шаблону.

9000
источник
6

Линтер не знает, что вы используете класс как миксин. Pylint знает, что вы используете миксин, если вы добавляете суффикс «миксин» или «миксин» в конце имени класса, тогда линтер перестает жаловаться.

linter_without_mixins linter2_with_mixin

Миксины сами по себе не плохие или хорошие, просто инструмент. Вы делаете их хорошим или плохим использованием.

dotoscat
источник
2
это больше похоже на комментарий, см. Как ответить
gnat
2
это ценный ответ, потому что я не думаю, что когда-либо узнал бы об этом намеке
кошка
1

Все ли в порядке с миксином?

Являются ли Python mixins анти-паттерном?

Миксины не приветствуются - они являются хорошим вариантом для множественного наследования.

Почему ваш линтер жалуется?

Pylint явно жалуется , потому что он не знает , где otherfunc, stackи my_var откуда.

Trivial?

Нет очевидной веской причины для того, чтобы разделить эти два метода на отдельные родительские классы, как в вашем примере в вопросе, так и в вашем более тривиальном связанном примере, показанном здесь.

201
202 class OpCore():
203     # nothing runs without these
204
205 class OpLogik(): 
206     # logic methods... 
207 
208 class OpString(): 
209     # string things
210 
211 
212 class Stack(OpCore, OpLogik, OpString): 
213 
214     "the mixin mixer of the above mixins" 

Затраты на миксины и шум

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

Вывод

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

Аарон Холл
источник
Есть веская причина разделять их в связанном, нетривиальном примере - мне проще организовать свой код, когда у меня есть часть полного класса, который реализует математику, другой, который реализует строки, и так далее, и вы смешиваете их вместе чтобы получить Форт Суп.
кот
«Другой ценой является то, что вы разделяете свой код на разные пространства имен, что может затруднить программистам понимание значения» - нет, это совсем не так. Пространства имен классов упрощают определение того, что делает группа методов, и когда кто-то хочет использовать стек, он должен вызывать / реализовывать Forth Soup, а не отдельные ингредиенты
cat
@cat, вероятно, потому что они придуманы и не используются в действительно большом проекте, ни один из примеров не поддерживает ваши идеи использования миксинов. Оба показывают только один раз, когда используется миксин, и в этом случае просто поместить реализацию в одном месте - это лучший стиль и организация. Теперь, если бы вы могли показать случай, когда вам нужны общие функции для разных классов, но вы не хотите использовать общий базовый класс, вы бы использовали миксин. Который стоит рядом с твоим долгим вопросом (UltraBird ответил на это хорошо). Но это отвечает на ваш вопрос, касающийся в целом применимости миксинов
Дламблин