嵌套使用双重装饰器时,如何获取正确的原函数名称?
解决双重装饰器下函数名称显示错误的问题
这个问题我之前也碰到过,本质是装饰器的执行顺序和元数据传递的问题,咱们一步步来解决:
问题根源
当你用@timing @profile的顺序时,实际执行逻辑是my_function = timing(profile(my_function))——也就是说timing装饰的是profile返回的包装函数,而不是原始的my_function。如果profile装饰器没有完全保留原始函数的元数据(或者某些版本里存在小问题),timing里的@wraps(f)只能继承profile包装函数的名称,就会出现显示wrapper的情况。
另外你代码里还有个小细节:time()最好写成time.time()(如果是导入import time的话),避免潜在的命名冲突。
解决方案
方案1:调整装饰器顺序(最简便)
把@profile放在@timing上面,这样timing先装饰原始函数,@wraps(f)会直接把wrapper的名称设为my_function,之后profile再装饰这个timing的包装函数。两边都能正常获取正确的函数名:
from memory_profiler import profile from functools import wraps import time def timing(f): @wraps(f) def wrapper(*args, **kwargs): start = time.time() result = f(*args, **kwargs) end = time.time() print('Elapsed time: {} - {}'.format(wrapper.__name__, end - start)) return result return wrapper # 调换顺序,先timing装饰原始函数,再profile装饰timing的包装函数 @profile @timing def my_function(): time.sleep(1) # 示例逻辑 my_function()
方案2:直接获取原始函数名称(不受顺序影响)
如果不想调整顺序,可以在timing装饰器里直接获取被装饰函数的名称,甚至递归拿到最原始的函数名(应对多层装饰的情况):
def timing(f): @wraps(f) def wrapper(*args, **kwargs): start = time.time() result = f(*args, **kwargs) end = time.time() # 方法1:直接用f.__name__(适用于大部分情况) func_name = f.__name__ # 方法2:递归获取最原始的函数名(应对多层装饰) # original_func = f # while hasattr(original_func, '__wrapped__'): # original_func = original_func.__wrapped__ # func_name = original_func.__name__ print('Elapsed time: {} - {}'.format(func_name, end - start)) return result return wrapper
方案3:升级memory_profiler
旧版本的memory_profiler可能存在装饰器不保留函数元数据的bug,执行pip install --upgrade memory_profiler升级到最新版本,说不定问题直接就解决了。
内容的提问来源于stack exchange,提问作者Sam Si
相关产品推荐
相关产品推荐

