获取装饰类方法的类名

12 投票
3 回答
11312 浏览
提问于 2025-04-16 12:16

考虑一下这个场景:

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

你可以看到它的输出结果,可以在这个链接中查看执行情况:http://codepad.org/Y4xXyjJO

>> foo
>> foo

我想知道有没有办法在第一行打印出 Test.foo,这样可以指明这个方法是属于哪个类的。

有什么想法吗?这可能吗?

提前谢谢你。

3 个回答

3

一个简单的版本:

def print_my_location(func):
    def wrapper(*args, **kwargs):
        print(f'[{func.__module__}::{func.__qualname__}]: ')
        return func(*args, **kwargs)

    return wrapper
4

这件事并不容易做到。如果你在内部函数的第一个参数里加上 self,那么你就可以用 self.__class__.__name__ 来获取类的名字。不过这样做会有问题:如果你装饰的是一个没有类的函数,而且这个函数没有参数的话,就会出错;如果有参数,它会把第一个参数当成 self

所以,除非有办法判断一个函数是否在对象的上下文中被调用,否则你想做的事情是行不通的。

顺便问一下,你为什么需要这个?听起来好像有更好的解决办法。

15

其实,你可以使用 inspect 模块来获取一个函数的签名。假设你遵循了一个约定,就是用第一个参数 'self' 来指代类对象,你可以这样做:

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

这样会打印出方法所属的模块、类和名称,或者如果这是一个函数的话,只会打印模块和名称。

从 Python 3.3 开始,你可以使用 fn.__qualname__ 来获取函数或方法的完整名称。

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

这个方法对函数和方法都很好用:

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

撰写回答