Декораторы классов в Python: практические примеры использования


9

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

@register
class MyPlugin(Plugin):
    pass

или

@recieves_notifications
class Console:
    def print(self, text):
        ...

Любые другие вменяемые случаи, о которых я думал, могли быть построены на основе методов наследования, метаклассов или декорирования. Не могли бы вы поделиться хорошими (или плохими!) Примерами использования декораторов классов?

Спасибо!


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

@amon, когда метакласс используется опытным разработчиком, это обычно означает, что другие варианты были тщательно рассмотрены, и метакласс пока является лучшим способом решения проблемы. Более того, явное лучше, чем неявное - это может быть причиной того, что (в качестве примера) мы имеем ABCMeta, а не @abstractclassдекоратор классов.
Заур Насибов

2
Я программист на Python и, честно говоря, не вижу в этом необходимости. Тот факт, что языковая функция существует, не означает, что вы должны или даже должны ее использовать. Дизайнеры придумывают всевозможные сумасшедшие функции, которые оказываются ненужными или даже наносят ущерб задним числом. На самом деле, вы упомянули две другие такие особенности в своем вопросе :)
gardenhead

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

Ответы:


8

При написании модульных тестов, имеющих @unittest.skipIfи @unittest.skipUnlessмогут использоваться как для отдельных методов, так и для целого unittest.TestCaseопределения класса. Это значительно облегчает написание тестов для конкретных платформ.

Пример из asyncio/test_selectors

# Some platforms don't define the select.kqueue object for these tests.
# On those platforms, this entire grouping of tests will be skipped.

@unittest.skipUnless(hasattr(selectors, 'KqueueSelector'),
                 "Test needs selectors.KqueueSelector)")
class KqueueSelectorTestCase(BaseSelectorTestCase, ScalableSelectorMixIn):
...

2

Декораторы классов были явно созданы, чтобы сделать некоторые вещи, которые уже могли быть выражены через метаклассы, более приятными, из PEP :

Мотивирующим сценарием использования было сделать некоторые конструкции более легко выраженными и менее зависимыми от деталей реализации интерпретатора CPython. Хотя можно выразить функциональность, подобную декоратору класса, с помощью метаклассов, результаты, как правило, неприятны, а реализация очень хрупкая. Кроме того, метаклассы наследуются, а декораторы классов - нет, что делает метаклассы непригодными для некоторых, специфичных для одного класса применений декораторов классов. Тот факт, что крупномасштабные проекты Python, такие как Zope, проходили эти дикие испытания, чтобы достичь чего-то вроде декораторов классов, победили BDFL.


Спасибо за цитирование PEP-3129. Однако вопрос не в том, почему существуют декораторы классов, что о них думает Гвидо и как Zope или IronPython могли использовать их до их появления. Речь идет об их практическом использовании сегодня, в 2016 году.
Заур Насибов

1
Правильно, дело в том, что исходный вопрос поднимает метаклассы как нечто, что можно использовать для реализации функциональности, подобной классу-декоратору, но суть декораторов в том, что их проще использовать и они более очевидны, чем метаклассы. Таким образом, ответ на эту часть вопроса: «Если вы разрываетесь между декораторами или метаклассами, вы должны использовать декораторы, потому что их легче понять другим».
quodlibetor

Вы оставляете больше вопросов, которые отвечают. «... весь смысл декораторов в том, что их проще использовать и они более очевидны, чем метаклассы» - в каких случаях? Тогда "... вы должны использовать декораторы, потому что другим легче их понять" Правда? За долгое время привыкания к Python я видел тонны метаклассов, некоторые уродливые, некоторые красивые. Но не обнаружили никаких декораторов класса вообще.
Заур Насибов

1

Из этого вопроса переполнения стека:

def singleton(class_):
  instances = {}
  def getinstance(*args, **kwargs):
    if class_ not in instances:
        instances[class_] = class_(*args, **kwargs)
    return instances[class_]
  return getinstance

@singleton
class MyClass(BaseClass):
  pass

Да, но противоположность дана прямо в том же вопросе SO: метакласс - лучший подход.
Заур Насибов

1

Поиск вариантов использования, которые можно выполнить с помощью декораторов классов, бесполезен - все, что можно сделать с помощью декораторов классов, можно сделать с помощью метаклассов. Даже ваш пример регистрации. Чтобы доказать это, вот метакласс, который применяет декоратор:

Python 2:

def register(target):
    print 'Registring', target
    return target


class ApplyDecorator(type):
    def __new__(mcs, name, bases, attrs):
        decorator = attrs.pop('_decorator')
        cls = type(name, bases, attrs)
        return decorator(cls)

    def __init__(cls, name, bases, attrs, decorator=None):
        super().__init__(name, bases, attrs)


class Foo:
    __metaclass__ = ApplyDecorator
    _decorator = register

Python 3:

def register(target):
    print('Registring', target)
    return target


class ApplyDecorator(type):
    def __new__(mcs, name, bases, attrs, decorator):
        cls = type(name, bases, attrs)
        return decorator(cls)

    def __init__(cls, name, bases, attrs):
        super().__init__(name, bases, attrs)


class Foo(metaclass=ApplyDecorator, decorator=register):
    pass

1
Как вы говорите - все можно сделать с помощью декоратора. Вопрос в том, какие случаи следует выполнять с помощью декораторов классов, а не метаклассов.
Заур Насибов
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.