Dekoratory klas w Pythonie: praktyczne przypadki użycia

9

Szukam praktycznych i niesyntetycznych przypadków użycia dekoratorów klasy Python. Jak dotąd jedynym przypadkiem, który miał dla mnie sens, było zarejestrowanie klasy w systemie wydawcy-subskrybenta, np. Wtyczek lub wydarzeń, coś w rodzaju:

@register
class MyPlugin(Plugin):
    pass

lub

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

Wszelkie inne rozsądne przypadki, o których myślałem, mogłyby zostać zbudowane na podstawie dziedziczenia, metaklas lub metod dekoracyjnych. Czy możesz podać jakieś dobre (lub złe!) Przykłady korzystania z dekoratorów klas?

Dziękuję Ci!

Zaur Nasibow
źródło
Na początek dekoratorzy są koncepcyjnie znacznie prostsi niż metaklasy. O wiele łatwiej jest napisać dekorator niż metaklasę i unika się konieczności wymyślania sposobu łączenia wielu metaklas bez rozbijania czegokolwiek.
amon
@amon, gdy doświadczony programista używa metaklasy, zwykle oznacza to, że dokładnie rozważono inne opcje, a metaklasa jest jak dotąd najlepszym sposobem rozwiązania problemu. Co więcej, jawne jest lepsze niż niejawne - może to być powód, dla którego (jako przykład) mamy ABCMeta, a nie @abstractclassdekorator klas.
Zaur Nasibov,
2
Jestem programistą Python i szczerze mówiąc, nie widzę takiej potrzeby. To, że istnieje funkcja języka, nie oznacza, że ​​musisz jej użyć, a nawet powinieneś. Projektanci wymyślają wszelkiego rodzaju szalone funkcje, które okazują się niepotrzebne lub nawet szkodliwe z perspektywy czasu. W swoim pytaniu wspomniałeś o dwóch innych takich funkcjach :)
ogrodnik
1
Nie jest czymś, bez czego nie mógłbym żyć, ale moim zdaniem użyteczne jest użycie dekoratora klasy do zarejestrowania wszystkich danych wejściowych i wyjściowych wszystkich metod klasy.
bgusach

Odpowiedzi:

8

Pisząc testy jednostkowe posiadające @unittest.skipIfi @unittest.skipUnlessmogą być użyte na poszczególnych metodach lub na całej unittest.TestCasedefinicji klasy. To znacznie ułatwia pisanie testów problemów specyficznych dla platformy.

Przykład z 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):
...
sethmlarson
źródło
2

Dekoratorzy klas zostali wyraźnie stworzeni, aby uczynić niektóre rzeczy, które były już możliwe do wyrażenia poprzez metaklasy, z PEP :

Motywujący przypadek użycia polegał na tym, aby niektóre konstrukcje były łatwiej wyrażane i mniej zależne od szczegółów implementacji interpretera CPython. Chociaż można wyrazić funkcjonalność klasy dekoratora za pomocą metaklas, wyniki są na ogół nieprzyjemne, a implementacja bardzo delikatna. Ponadto metaklasy są dziedziczone, podczas gdy dekoratory klas nie, co czyni metaklasy nieodpowiednimi do niektórych, specyficznych dla jednej klasy zastosowań dekoratorów klas. Fakt, że na dużą skalę projekty Pythona, takie jak Zope, przechodził przez te dzikie wysiłki, aby osiągnąć coś w rodzaju dekoratorów klas, wygrał z BDFL.

quodlibetor
źródło
Dzięki za zacytowanie PEP-3129. Jednak pytanie nie dotyczy tego, dlaczego istnieją dekoratorzy klas, co Guido o nich myśli i jak Zope lub IronPython mogliby ich użyć, zanim się pojawili. Pytanie dotyczy ich praktycznego wykorzystania dzisiaj, w 2016 r.
Zaur Nasibov
1
Tak, chodzi o to, że pierwotne pytanie przywołuje metaklasy jako coś, co można wykorzystać do implementacji funkcjonalności podobnej do dekoratorów klas, ale sedno dekoratorów polega na tym, że są łatwiejsze w użyciu i bardziej oczywiste niż metaklasy. Odpowiedź na tę część pytania brzmi: „Jeśli jesteś rozdarty między dekoratorami lub metaklasami, powinieneś używać dekoratorów, ponieważ są łatwiejsze do zrozumienia dla innych”.
quodlibetor
Zostawiasz więcej pytań, na które odpowiedzi. „... sedno dekoratorów polega na tym, że są łatwiejsze w użyciu i bardziej oczywiste niż metaklasy” - w jakich przypadkach? W takim razie „… powinieneś użyć dekoratorów, ponieważ łatwiej jest zrozumieć innym” Naprawdę? Przez długi czas uzależnienia od Pythona widziałem mnóstwo metaklas, niektóre brzydkie, inne piękne. Ale w ogóle nie odkryłem żadnych dekoratorów klas.
Zaur Nasibov
1

Z tego pytania o przepełnienie stosu:

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
Jared Smith
źródło
Tak, ale kontra jest podana dokładnie w tym samym pytaniu SO: metaklasa jest lepszym podejściem.
Zaur Nasibov,
1

Poszukiwanie przypadków użycia z dekoratorami na zajęciach jest daremne - wszystko , co można zrobić z dekoratorami na zajęciach, można wykonać za pomocą metaklas. Nawet twój przykład rejestracji. Aby to udowodnić, oto metaklasa, która stosuje dekoratora:

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
Idan Arye
źródło
1
Jak mówisz - z dekoratorem wszystko można zrobić. Pytanie brzmi: jakie są przypadki, które należy wykonać za pomocą dekoratorów klas, a nie metaklas.
Zaur Nasibov