Search code examples
pythondecorator

Get the class name of a decorated class method


Consider this scenario:

import functools

def wrapmethod(f):
    @functools.wraps(f)
    def wrap(*args, **kwargs):
        print '>> %s' % (f.func_name)

        # Here I'll do pre-processing
        r = f(*args, **kwargs)
        # Here I'll do post-processing

        return r

    return wrap

@wrapmethod
def foo():
    pass

class Test(object):
    @wrapmethod
    def foo(self):
        pass

test = Test()
test.foo()
foo()

It'll output this, as you can see executed in http://codepad.org/Y4xXyjJO:

>> foo
>> foo

I want to know a way to print out Test.foo in the first line, indicating the class which the method is linked to.

Any ideas? Is it ever possible?

Thank you in advance.


Solution

  • In fact, you can use inspect module to get the signature of a function, and supposing that you are following the convention of referring to the class object by the first argument 'self', you can do the following :

    import inspect  
    def print_name(*_args):
        def _print_name(fn):
            def wrapper(*args, **kwargs):
                try :
                    is_method   = inspect.getargspec(fn)[0][0] == 'self'
                except :
                    is_method   = False
    
                if is_method :
                    name    = '{}.{}.{}'.format(fn.__module__, args[0].__class__.__name__, fn.__name__)
                else :
                    name    = '{}.{}'.format(fn.__module__, fn.__name__)
    
                print (name)
                return  fn(*args,**kwargs)
            return wrapper
        return _print_name
    

    this will print the method module, class and name or just the module and name, if this is a function

    From python 3.3 onward, fn.__qualname__ can be used to get the qualified name of the function/method.

    def print_name(*args):
         def _print_name(fn):
             def wrapper(*args, **kwargs):
                 print('{}.{}'.format(fn.__module__, fn.__qualname__))
                 return fn(*args, **kwargs)
             return wrapper
         return _print_name
    

    This works fine with both functions and methods :

    In [1]: class A():
    ...:     @print_name()
    ...:     def a():
    ...:         print('Hi from A.a')
    In [2]: A.a()
        __main__.A.a
        Hi from A.a
    
    In [25]: @print_name()
        ...: def b():
        ...:     print('Hi from b')
        ...: 
    In [26]: b()
        __main__.b
        Hi from b