Декораторы против наследства - PullRequest
15 голосов
/ 20 июня 2011

Как вы решаете между использованием декораторов и наследованием, когда возможны оба варианта?

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

Мне особенно интересен Python.

Ответы [ 4 ]

20 голосов
/ 20 июня 2011

Декораторы ...:

  • ... следует использовать, если вы пытаетесь "обернуть".Обтекание состоит из взятия чего-либо, изменения (или регистрации чего-либо) и / или возврата прокси-объекта, который ведет себя «почти точно», как оригинал.
  • ... подходят для применения подобного миксину поведения,пока вы не создаете большой стек прокси-объектов.
  • ... имеют подразумеваемую абстракцию "стека":

например,

@decoA
@decoB
@decoC
def myFunc(...): ...
    ...

Эквивалентно:

def myFunc(...): ...
    ...
myFunc = decoA(decoB(decoC(myFunc)))  #note the *ordering*

Множественное наследование ...:

  • ... лучше всего подходит для добавления методов в классы;Вы не можете использовать это, чтобы украсить функции легко.В этом контексте его можно использовать для достижения миксиноподобного поведения, если все, что вам нужно, это набор дополнительных методов типа «утка».
  • ... может быть немного громоздким, если ваша проблема неЭто хорошо подходит для проблем с конструкторами суперклассов и т. д. Например, метод подклассов __init__ не будет вызываться, если он не вызывается явно (через протокол порядка разрешения методов)!

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

Вещи, для которых вы часто найдете декораторы (например, для запоминания), также являются хорошими кандидатами, но их следует использовать при модерации, если они возвращают прокси-объекты;Порядок их применения имеет значение.И слишком много декораторов друг над другом используют их таким образом, что они не предназначены для использования.

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

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

3 голосов
/ 20 июня 2011

Проблема, на которую вы ссылаетесь, заключается не в выборе между декораторами и классами. это с использованием декораторов, но у вас есть возможность использовать либо:

  • декоратор, который возвращает класс
  • декоратор, который возвращает функцию

Декоратор - это просто причудливое имя для шаблона "обертка", то есть замена чего-то другим. Реализация зависит от вас (класс или функция).

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

  • при украшении функции вы можете предпочесть декораторы, которые возвращают прокси-функции
  • при украшении класса вы можете предпочесть декораторы, которые возвращают прокси-классы

(Почему это хорошая идея? Могут быть предположения, что декорированная функция по-прежнему является функцией, а декорированный класс по-прежнему является классом.)

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

edit: После лучшего понимания вашего вопроса я выложил другое решение на Python functools.wraps эквивалент для классов

1 голос
/ 13 февраля 2014

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

Давайте возьмем кеширование в качестве примера. Если вы хотите добавить средство кэширования к двум классам в вашей системе: A и B с наследованием, вы, вероятно, в конечном итоге получите ACached и BCached. Переопределив некоторые методы в этих классах, вы, вероятно, продублируете множество кодов для одной и той же логики кэширования. Но если вы используете декоратор в этом случае, вам нужно определить только один декоратор для декорирования обоих классов.

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

1 голос
/ 20 июня 2011

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

...