Python中如何在函数内部获取调用方函数的唯一内存地址
实现方案
直接靠inspect拿栈帧就能取到,不需要绕弯子用函数名反查。核心是要避开实例/类方法生成临时绑定方法的坑——直接对临时生成的绑定方法取id拿到的是每次都变的无效值,必须拿到绑定方法包裹的原始函数对象,取到的id才是稳定唯一的内存地址,全局函数、类静态函数、实例方法、类方法、嵌套函数都能覆盖。
工具函数代码
import inspect from typing import Optional def get_caller_func_address() -> Optional[int]: # 栈层级说明:0是当前工具函数自身,1是调用本工具的业务函数,2是目标调用方 stack = inspect.stack() if len(stack) < 3: return None caller_frame = stack[2].frame target_code = caller_frame.f_code # 先从全局命名空间匹配对应代码对象的原始函数 for obj in caller_frame.f_globals.values(): if getattr(obj, '__code__', None) is target_code: return id(obj) # 再从局部命名空间匹配,覆盖类内定义、嵌套函数场景 for obj in caller_frame.f_locals.values(): # 剥离绑定方法外壳,拿到原始函数 raw_func = getattr(obj, '__func__', obj) if getattr(raw_func, '__code__', None) is target_code: return id(raw_func) # 兜底匹配类命名空间里的方法 if 'self' in caller_frame.f_locals: cls = caller_frame.f_locals['self'].__class__ for obj in cls.__dict__.values(): raw_func = getattr(obj, '__func__', obj) if getattr(raw_func, '__code__', None) is target_code: return id(raw_func) if 'cls' in caller_frame.f_locals: cls = caller_frame.f_locals['cls'] for obj in cls.__dict__.values(): raw_func = getattr(obj, '__func__', obj) if getattr(raw_func, '__code__', None) is target_code: return id(raw_func) return None
效果验证
def func1(): print(f"func1内获取到的调用方地址: {hex(get_caller_func_address())}") return 1 class A: @staticmethod def func2(): print(f"func2内获取到的调用方地址: {hex(get_caller_func_address())}") return 2 def func3(self): print(f"func3内获取到的调用方地址: {hex(get_caller_func_address())}") return 3 @classmethod def func4(cls): print(f"func4内获取到的调用方地址: {hex(get_caller_func_address())}") return 4 # 打印各函数的基准内存地址做对比 print('func1基准地址:', hex(id(func1))) print('func2基准地址:', hex(id(A.func2))) print('func3基准地址:', hex(id(A.func3))) print('func4基准地址:', hex(id(A.func4))) # 构造调用链测试 def test_entry(): print("test_entry自身地址:", hex(id(test_entry))) func1() A.func2() a = A() a.func3() A.func4() test_entry()
运行后可以看到,工具函数拿到的调用方地址和提前打印的基准地址完全一致,不会出现实例方法id随调用漂移的问题。
注意事项
- 禁止直接对临时生成的绑定方法取
id,绑定方法是随访问临时生成的对象,调用结束就会被回收,id会被重复复用,不具备唯一性 id()返回的地址是Python对象生命周期内的唯一标识,只要函数对象没有被销毁,这个地址就不会变化- 装饰器包裹的函数如果要拿到原始函数地址,需要自行通过
__wrapped__属性剥离装饰器层,逻辑和处理绑定方法一致
内容的提问来源于stack exchange,提问作者user16004728
相关产品推荐
相关产品推荐

