Подтвердить что ты не робот

Декораторы против наследования

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

Например, эта проблема имеет два решения.

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

4b9b3361

Ответ 1

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

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

например.

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

Является эквивалентным:

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

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

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

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

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

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

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

Ответ 2

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

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

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

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

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

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

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

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

Ответ 3

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

Ответ 4

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

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

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