Есть в Python такая штука, как декораторы. По сути, это обёртка для класса или функции, которая позволяет обслуживать создание экземпляров классов или вызов функций и оборачивать их дополнительной логикой.
Есть в них несколько тонкостей, но одна из них у меня вызвала затруднение, разрешением которого я и хочу поделиться.
Декоратор может быть определён как класс или как функция. И если определить декоратор класса как класс, то возникает одна проблема: создание каждого нового экземпляра затирает предыдущий. Для того, чтобы выяснить причину этого, я немного адаптировал пример из книжки Лутца "Изучаем Python".
Вот что происходит, если декоратор определить как функцию:
>>>def decorator(cls):
print('in func; cls='+str(cls))class Wrapper:
def__init__(self, *args):
print('in class init')self.wrapped=cls(*args)print('self='+str(self)+' type(self)='+str(type(self))+'\nself.wrapped='+str(self.wrapped)+' type(self.wrapped)='+str(type(self.wrapped))+'\ncls='+str(cls)+' type(cls)='+str(type(cls)))def__getattr__(self,name):
returngetattr(self.wrapped,name)print(str(Wrapper)+'\t'+str(type(Wrapper)))return Wrapper
>>> @decorator
class C:
def__init__(self, *args):
self.attr=args
in func; cls=<class '__main__.C'><class '__main__.Wrapper'><class 'type'>>>> C, type(C)(<class '__main__.Wrapper'>, <class 'type'>)>>> x=C('asdf')inclass init
self=<__main__.Wrapperobject at 0x010C9B90>type(self)=<class '__main__.Wrapper'>self.wrapped=<__main__.Cobject at 0x010C9CD0>type(self.wrapped)=<class '__main__.C'>
cls=<class '__main__.C'>type(cls)=<class 'type'>>>> y=C('asdf')inclass init
self=<__main__.Wrapperobject at 0x010C7DB0>type(self)=<class '__main__.Wrapper'>self.wrapped=<__main__.Cobject at 0x010C93D0>type(self.wrapped)=<class '__main__.C'>
cls=<class '__main__.C'>type(cls)=<class 'type'>>>> x,type(x)(<__main__.Wrapperobject at 0x010C9B90>, <class '__main__.Wrapper'>)>>> y,type(y)(<__main__.Wrapperobject at 0x010C7DB0>, <class '__main__.Wrapper'>)
Как мы видим, на этапе использования декоратора создается только объект класса, но не экземпляр класса Wrapper, оригинальный же класс сохраняется в локальной области видимости функции в процессе компиляции (не вызова) объекта класса. А вот уже при создании экзепляров декорированного класса С, по сути экземпляров класса Wrapper, создаются разные экземпляры и объектов С и оборачивающего его Wrapper, как можно видеть по выводимым адресам.
Что же происходит, если определить декоратор как класс?
>>>class Decorator:
def__init__(self, C):
print('Decorator init')self.C=C
print('self='+str(self)+' type(self)='+str(type(self))+'\nself.C='+str(self.C)+' type(self.C)='+str(type(self.C))+'\nC='+str(C)+' type(C)='+str(type(C)))def__call__(self,*args):
print('Decorator call')self.wrapped=self.C(*args)print('self='+str(self)+' type(self)='+str(type(self))+'\nself.wrapped='+str(self.wrapped)+' type(self.wrapped)='+str(type(self.wrapped))+'\ncls='+str(self.C)+' type(self.C)='+str(type(self.C)))returnselfdef__getattr__(self, attrname):
returngetattr(self.wrapped,attrname)>>> @Decorator
class C: pass
Decorator init
self=<__main__.Decoratorobject at 0x010A9FB0>type(self)=<class '__main__.Decorator'>self.C=<class '__main__.C'>type(self.C)=<class 'type'>
C=<class '__main__.C'>type(C)=<class 'type'>>>> x=C()
Decorator call
self=<__main__.Decoratorobject at 0x010A9FB0>type(self)=<class '__main__.Decorator'>self.wrapped=<__main__.Cobject at 0x00C70830>type(self.wrapped)=<class '__main__.C'>
cls=<class '__main__.C'>type(self.C)=<class 'type'>>>> y=C()
Decorator call
self=<__main__.Decoratorobject at 0x010A9FB0>type(self)=<class '__main__.Decorator'>self.wrapped=<__main__.Cobject at 0x010A9FF0>type(self.wrapped)=<class '__main__.C'>
cls=<class '__main__.C'>type(self.C)=<class 'type'>>>> x,type(x),y,type(y)(<__main__.Decoratorobject at 0x010A9FB0>, <class '__main__.Decorator'>, <__main__.Decoratorobject at 0x010A9FB0>, <class '__main__.Decorator'>)
Тут мы видим, что на этапе декорирования создаётся [b]экземпляр[/b] класса Decorator! И при создании экземпляров декорируемого класса будет вызываться метод __call__, которому каждый раз будет передаваться данный экземпляр класса Decorator и будет затираться его атрибут wrapped.
Но важно отметить, что если мы применим этот декоратор к другому классу это не затрет наш предыдущий декоратор. Да это и понятно, так как в данном случае будет просто создан ещё один экземпляр класса Decorator:
>>> @Decorator
class C: pass
Decorator init
self=<__main__.Decoratorobject at 0x010A9FB0>type(self)=<class '__main__.Decorator'>self.C=<class '__main__.C'>type(self.C)=<class 'type'>
C=<class '__main__.C'>type(C)=<class 'type'>>>> x=C()
Decorator call
self=<__main__.Decoratorobject at 0x010A9FB0>type(self)=<class '__main__.Decorator'>self.wrapped=<__main__.Cobject at 0x00C70830>type(self.wrapped)=<class '__main__.C'>
cls=<class '__main__.C'>type(self.C)=<class 'type'>>>> @Decorator
class D: pass
Decorator init
self=<__main__.Decoratorobject at 0x010B1030>type(self)=<class '__main__.Decorator'>self.C=<class '__main__.D'>type(self.C)=<class 'type'>
cls=<class '__main__.D'>type(C)=<class 'type'>>>> y=D()
Decorator call
self=<__main__.Decoratorobject at 0x010B1030>type(self)=<class '__main__.Decorator'>self.wrapped=<__main__.Dobject at 0x00C70830>type(self.wrapped)=<class '__main__.D'>
cls=<class '__main__.D'>type(self.C)=<class 'type'>>>> y,type(y)(<__main__.Decoratorobject at 0x010B1030>, <class '__main__.Decorator'>)
сколько не искал по интернету, но нигде не описываются стандартные декораторы наподобие staticmethod, очень жаль... было бы очень замечательно если бы вы продолжили эту тему и описали встроенные декораторы
да, их применение... я ни как не могу найти полный пречень всех встренных декораторов, полазил по докам, и что-то как то мало нарыл : @classmethod @staticmethod @property
Есть хорошая книжка Марка Лутца "Изучаем Python", там есть примеры их применения с объяснениями. Она у меня сейчас не под рукой, но на следующей неделе могу выложить оттуда примеры применения
Я как-то вопрос не внимательно прочитал. Так вроде других встроенных декораторов-то и нет, как и необходимости в них. Они же достаточно просто сами реализуются. А примеры по тем трём, которые Вы нашли есть в стандартной документации.
да я их оттуда и выдернул.., но у меня нет опыта их применения- это и усложняет понимание, да..было бы здорова на пальцах обьяснить стандарные декораторы, они ведь тоже часто используются... спс за автора, такую книжку знаю, но в нее не лазил
сколько не искал по интернету, но нигде не описываются стандартные декораторы наподобие staticmethod, очень жаль... было бы очень замечательно если бы вы продолжили эту тему и описали встроенные декораторы
ОтветитьУдалитьто есть? их реализацию или их применение?
Удалитьда, их применение... я ни как не могу найти полный пречень всех встренных декораторов, полазил по докам, и что-то как то мало нарыл :
Удалить@classmethod
@staticmethod
@property
Есть хорошая книжка Марка Лутца "Изучаем Python", там есть примеры их применения с объяснениями. Она у меня сейчас не под рукой, но на следующей неделе могу выложить оттуда примеры применения
УдалитьЯ как-то вопрос не внимательно прочитал. Так вроде других встроенных декораторов-то и нет, как и необходимости в них. Они же достаточно просто сами реализуются.
УдалитьА примеры по тем трём, которые Вы нашли есть в стандартной документации.
да я их оттуда и выдернул.., но у меня нет опыта их применения- это и усложняет понимание, да..было бы здорова на пальцах обьяснить стандарные декораторы, они ведь тоже часто используются...
Удалитьспс за автора, такую книжку знаю, но в нее не лазил
тогда лучше там и посмотреть - лучше Лутца это не объяснить)
Удалить