Can a decorator of an instance method access the class?

后端 未结 13 952
没有蜡笔的小新
没有蜡笔的小新 2020-11-27 12:33

I have something roughly like the following. Basically I need to access the class of an instance method from a decorator used upon the instance method in its definition.

13条回答
  •  情话喂你
    2020-11-27 12:57

    Function doesn't know whether it's a method at definition point, when the decorator code runs. Only when it's accessed via class/instance identifier it may know its class/instance. To overcome this limitation, you may decorate by descriptor object to delay actual decorating code until access/call time:

    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)
    

    This allows you to decorate individual (static|class) methods:

    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
    

    Now you can use decorator code for introspection...

    >>> 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
    

    ...and for changing function behavior:

    >>> 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'}
    

提交回复
热议问题