Декоратор скрывает self
Я пытаюсь создать декоратор, который похож на property
для своих задач, но моя реализация, которая подобна property
на чистом Python не работает. Я не могу получить доступ к переменным класса до оборачивания через self
. Вот минимальный воспроизводимый пример:
class MyWrapper(object):
def __init__(self, first=None, second=None) -> None:
self.first = first
self.second = second
def __call__(self, *args, **kwargs):
if self.first is not None:
self.first(*args, **kwargs)
if self.second is not None:
self.second(*args, **kwargs)
def second_handler(self, func):
wrapper = type(self)(self.first, func)
return wrapper
class TestClass(object):
def __init__(self, name) -> None:
self.name = name
@MyWrapper
def foo(self):
print(f'Hello, {self.name}!')
@foo.second_handler
def foo(self):
print(f'Bye, {self.name}!')
def main(*args, **kwargs):
instance = TestClass('User')
instance.foo()
if __name__ == '__main__':
main()
Должен вывести:
Hi User!
Bye, User!
В момент вызова, self
подменяется декоратором и я теряю связь с атрибутами исходного класса.
Ответы (1 шт):
Нужно реализовать дескриптор — специальный класс, который содержит метод __get__
(и другие методы, например __set__
, если нужно переопределить присваивание атрибуту объекта). Такой класс может быть использован как декоратор для методов. Собственно в примере по вашей ссылке property
и реализован как дескриптор (методы __get__
, __set__
, __delete__
, __set_name__
- это специфические для дескрипторов методы).
Пришлось немного поколодовать, потому что сходу не вспомнил как это работает, минимальный пример такой:
class MyWrapper:
_second_handler = None
def __init__(self, method):
self.method = method
def __get__(self, obj, objtype=None):
self.obj = obj
return self
def __call__(self, *args, **kwargs):
self.method(self.obj, *args, **kwargs)
if self._second_handler is not None:
self._second_handler(self.obj, *args, **kwargs)
def second_handler(self, method):
self._second_handler = method
return self
class TestClass:
def __init__(self, name) -> None:
self.name = name
@MyWrapper
def foo(self):
print(f'Hello, {self.name}!')
@foo.second_handler
def foo(self):
print(f'Bye, {self.name}!')
def main():
instance = TestClass('User')
instance.foo()
if __name__ == '__main__':
main()
Тут __get__
будет вызван при попытке обращения к атрибуту foo
, в него же в параметр obj
передастся объект, у которого атрибут запрашивается, его нужно сохранить и использовать как self
при вызове сохраненных методов.