Декоратори класу в 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

Так, але контраст дається правильно в тому ж запитанні ТА: метаклас - це кращий підхід.
Заур Насібов

1

Шукати випадки використання, які можна зробити з декораторами на заняттях, марно - все, що можна зробити з декораторами на заняттях, можна зробити з метакласами. Навіть ваш приклад реєстрації. Щоб довести це, ось метаклас, який застосовує декоратор:

Пітон 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

Пітон 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
Як ви кажете - з декоратором можна зробити все, що завгодно. Питання полягає в тому, які випадки слід робити за допомогою декораторів класів, а не метакласів.
Zaur Nasibov
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.