Доступ к связанному методу или к себе при украшении метода - PullRequest
0 голосов
/ 02 октября 2018

У меня есть сценарий использования, в котором я хочу украсить метод дополнительным способом его вызова, например, в этом коде:

def decorator(func):
    def enhanced(*args, **kwargs):
        func(*args, **kwargs)

    func.enhanced = enhanced
    return func

@decorator
def function():
    pass

class X:
    @decorator
    def function(self):
        pass

x = X()

function()
function.enhanced()
x.function()
# x.function.enhanced()
x.function.enhanced(x)

Первые три вызова работают, как и ожидалось, но x.function.enhanced()не;Мне нужно написать x.function.enhanced(x), чтобы все заработало.Я знаю, что это потому, что func, передаваемый в декоратор, является не связанным методом, а функцией, и, таким образом, должен передаваться self явно.

Но как мне обойти это?Из того, что я немного понимаю о дескрипторах, они важны только при поиске класса, и, поскольку func не является классом, func.enhanced не ищется таким образом, чтобы я мог его перехватить.

Есть ли что-то, что я могу сделать здесь?

Ответы [ 2 ]

0 голосов
/ 02 октября 2018

Просто в качестве примера того, что я имею в виду в комментариях к ответу, опубликованному @blhsing:

class EnhancedProperty:
    def __init__(self, func):
        self.func = func
    def enhanced(self, *args, **kwargs):
        return self.func(*args, **kwargs)
    def __call__(self, *args, **kwargs):
        return self.func(*args, **kwargs)
    def __get__(self, obj, typ):
        return Enhanced(self.func, obj, typ)

class Enhanced:
    def __init__(self, func, obj, typ):
        self.func = func
        self.obj = obj
        self.typ = typ
    def __call__(self, *args, **kwargs):
        return self.func.__get__(self.obj, self.typ)(*args, **kwargs)
    def enhanced(self, *args, **kwargs):
        return self.func(self.obj, *args, **kwargs)

def decorator(f):
    return EnhancedProperty(f)

В REPL:

In [2]: foo(8, -8)
Out[2]: 1040

In [3]: foo.enhanced(8, -8)
Out[3]: 1040

In [4]: Bar().baz('foo')
Out[4]: ('foo', 'foo')

In [5]: Bar().baz.enhanced('foo')
Out[5]: ('foo', 'foo')
0 голосов
/ 02 октября 2018

Вы можете вернуть дескриптор , который возвращает объект, который вызывает саму себя и имеет атрибут enhanced, сопоставленный с вашей enhanced функцией-оболочкой:

from functools import partial
def decorator(func):
    class EnhancedProperty:
        # this allows function.enhanced() to work
        def enhanced(self, *args, **kwargs):
            print('enhanced', end=' ') # this output is for the demo below only
            return func(*args, **kwargs)
        # this allows function() to work
        def __call__(self, *args, **kwargs):
            return func(*args, **kwargs)
        def __get__(self, obj, objtype):
            class Enhanced:
                # this allows x.function() to work
                __call__ = partial(func, obj)
                # this allows x.function.enhanced() to work
                enhanced = partial(self.enhanced, obj)
            return Enhanced()
    return EnhancedProperty()

, так что:

@decorator
def function():
    print('function')

class X:
    @decorator
    def function(self):
        print('method of %s' % self.__class__.__name__)

x = X()

function()
function.enhanced()
x.function()
x.function.enhanced()

выдаст:

function
enhanced function
method of X
enhanced method of X
...