Функция не знает, является ли это методом в точке определения, когда выполняется код декоратора. Только когда к нему обращаются через идентификатор класса / экземпляра, он может знать свой класс / экземпляр. Чтобы преодолеть это ограничение, вы можете декорировать объект дескриптора, чтобы задержать фактический код декорирования до времени доступа / вызова:
class decorated(object):
def __init__(self, func, type_=None):
self.func = func
self.type = type_
def __get__(self, obj, type_=None):
func = self.func.__get__(obj, type_)
print('accessed %s.%s' % (type_.__name__, func.__name__))
return self.__class__(func, type_)
def __call__(self, *args, **kwargs):
name = '%s.%s' % (self.type.__name__, self.func.__name__)
print('called %s with args=%s kwargs=%s' % (name, args, kwargs))
return self.func(*args, **kwargs)
Это позволяет вам декорировать отдельные (статические | класс) методы:
class Foo(object):
@decorated
def foo(self, a, b):
pass
@decorated
@staticmethod
def bar(a, b):
pass
@decorated
@classmethod
def baz(cls, a, b):
pass
class Bar(Foo):
pass
Теперь вы можете использовать код декоратора для самоанализа ...
>>> Foo.foo
accessed Foo.foo
>>> Foo.bar
accessed Foo.bar
>>> Foo.baz
accessed Foo.baz
>>> Bar.foo
accessed Bar.foo
>>> Bar.bar
accessed Bar.bar
>>> Bar.baz
accessed Bar.baz
... и для изменения поведения функции:
>>> Foo().foo(1, 2)
accessed Foo.foo
called Foo.foo with args=(1, 2) kwargs={}
>>> Foo.bar(1, b='bcd')
accessed Foo.bar
called Foo.bar with args=(1,) kwargs={'b': 'bcd'}
>>> Bar.baz(a='abc', b='bcd')
accessed Bar.baz
called Bar.baz with args=() kwargs={'a': 'abc', 'b': 'bcd'}