함수는 데코레이터 코드가 실행될 때 정의 지점에서 메서드인지 여부를 알지 못합니다. 클래스 / 인스턴스 식별자를 통해 액세스 할 때만 해당 클래스 / 인스턴스를 알 수 있습니다. 이 한계를 극복하기 위해, 접근 / 호출 시간까지 실제 장식 코드를 지연시키기 위해 서술자 객체로 장식 할 수 있습니다 :
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'}