Доступ к self в атрибуте функции
Я пытаюсь добавить декоратор, который добавляет вызываемые атрибуты к функциям, которые возвращают немного разные объекты, чем возвращаемое значение функции, но будут выполнять функцию в какой-то момент.
проблема, с которой я сталкиваюсь, заключается в том, что когда объект функции передается в декоратор, он развязывается и не содержит неявного
2 ответов
можно использовать дескрипторов здесь:
class deco(object):
def __init__(self, func):
self.func = func
self.parent_obj = None
def __get__(self, obj, type=None):
self.parent_obj = obj
return self
def __call__(self, *args, **kwargs):
return self.func(self.parent_obj, *args, **kwargs)
def string(self, *args, **kwargs):
return str(self(*args, **kwargs))
class Test(object):
def __init__(self, value):
self._value = value
@deco
def plus(self, n):
return self._value + n
так что:
>>> test = Test(3)
>>> test.plus(1)
4
>>> test.plus.string(1)
'4'
это требует объяснения. deco
является декоратором, но это также дескриптор. Дескриптор-это объект, который определяет альтернативное поведение, вызываемое при поиске объекта как атрибута его родителя. Интересно, что методы bounds сами реализуются с использованием протокола дескриптора
это полный рот. Давайте посмотрим, что происходит, когда мы запускаем пример кода. Во-первых, когда мы определяем plus
метод, мы применяем deco
оформителя. Теперь обычно мы видим функции в качестве декораторов, а возвращаемое значение функции-это украшенный результат. Здесь мы используем класс в качестве декоратора. В результате Test.plus
- это не функция, а экземпляр на deco
тип. Этот экземпляр содержит ссылку на plus
функция, которую мы хотим обернуть.
в deco
класс __call__
метод, который позволяет экземплярам его действовать как функции. Эта реализация просто передает аргументы, приведенные в plus
функция имеет ссылку на. Обратите внимание, что первым аргументом будет ссылка на Test
экземпляра.
хитрая часть приходит в осуществлении test.plus.string(1)
. Для этого нам нужна ссылка на test
экземпляр которого plus
экземпляра атрибута. Для этого мы используем протокол дескриптора. Что есть, определяем a __get__
метод, который будет вызываться всякий раз, когда deco
экземпляр доступен как атрибут некоторого экземпляра родительского класса. Когда это происходит, он сохраняет родительский объект внутри себя. Тогда мы можем просто реализовать plus.string
как метод на deco
класс и используйте ссылку на родительский объект, хранящийся в deco
например, чтобы добраться до test
экземпляра plus
принадлежит.
это много магии, так что вот отказ от ответственности: хотя это выглядит круто, это, вероятно, не отличная идея для реализации чего-то подобного.
вам нужно украсить свою функцию во время создания экземпляра (перед созданием метода экземпляра). Вы можете сделать это, переопределив __new__
способ:
class Test(object):
def __new__(cls, *args_, **kwargs_):
def deco(func):
def string(*args, **kwargs):
return "my_str is :" + str(func(*args, **kwargs))
# *1
func.__func__.string = string
return func
obj = object.__new__(cls, *args_, **kwargs_)
setattr(obj, 'plus', deco(getattr(obj, 'plus')))
return obj
def __init__(self, value):
self._value = 1
def plus(self, n):
return self._value + n
демо:
>>> t = Test(100)
>>> t.plus(1)
>>> t.plus.string(5)
>>> 'my_str is :6'
1. Поскольку python не позволяет вам получить доступ к атрибуту реального экземпляра во время установки, вы можете использовать __func__
метод для доступа к объекту реальной функции метода экземпляра.