Доступ к 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__ метод для доступа к объекту реальной функции метода экземпляра.