You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Python类实例方法内存地址(id返回值)频繁变化原因咨询

问题现象
  • 调试包含多个私有内部方法的大型Python模块时,通过VSCode调试器观察到:单步执行任意代码(包括与这些内部方法无关的代码段)时,所有内部方法的内存地址都会发生改变,最初曾怀疑代码存在严重Bug。
  • 经过排查,脱离VSCode、不依赖调试器的纯命令行环境下可复现该问题,复现代码如下:
class MyClass():

    def __init__(self):
        self._internal()

    def _internal(self):
        for i in range(10):
            print(i, f'id={id(self._internal)}')

x = MyClass()
  • 多次运行上述测试代码时,_internal方法的id值有时保持静态,有时会在for循环执行过程中出现地址反复变动的情况,命令行直接运行的典型输出如下:
> "\Program Files\Python310"\python internal_method.py
0 id=2298435929536
1 id=2298435929728
2 id=2298435929536
3 id=2298435929728
4 id=2298435929536
5 id=2298435929728
6 id=2298435929536
7 id=2298435929728
8 id=2298435929536
9 id=2298435929728
  • 在VSCode调试器中单步执行该循环时,地址变化会更加无规律,调试启动后的典型输出如下:
>  c: && cd c:\Code\#Scrap && cmd /C ""C:\Program Files\Python310\python.exe" c:\Users\blah\.vscode\extensions\ms-python.python-2022.8.0\pythonFiles\lib\python\debugpy\launcher 50429 -- c:\Code\#Scrap\internal_method.py "
0 id=2341889160512
1 id=2341898208576
2 id=2341886959552
3 id=2341898111680
4 id=2341898210112
5 id=2341850994176
6 id=2341898298048
7 id=2341889399040
8 id=2341850998208
9 id=2341898172800
根本成因

该现象是CPython的标准实现机制导致,既不是代码Bug,也不是调试器故障:

  • “函数内存地址固定”的认知仅适用于类定义完成后,类本身持有的原始函数对象(即MyClass._internal),该对象的内存地址在类加载完成后不会发生变化。
  • 当通过实例访问方法(即self._internal)时,Python的方法描述符机制会在每次属性访问时临时生成一个新的绑定方法(bound method)对象,该对象会同时绑定当前实例引用与原始函数引用,实现调用时自动传入self参数的逻辑。
  • 代码中每次执行id(self._internal)时,都会触发一次属性访问、生成一个全新的绑定方法对象;上一次访问生成的绑定方法因为没有被任何变量持有引用,会立刻被垃圾回收,其占用的内存地址会被内存分配器回收复用。
    • 命令行运行时两个id交替出现,是因为该场景下内存分配逻辑简单,刚好反复复用两个固定内存块存放新生成的绑定方法。
    • VSCode调试环境下,调试器会注入大量额外运行时对象、持有更多临时引用,内存分配的随机性更强,因此观测到的id值变化更无规律。
验证方式

运行以下代码可直接验证上述逻辑:

class MyClass():
    def _internal(self):
        pass

obj = MyClass()
# 类持有的原始函数id始终固定
print(f"类属性访问函数id1: {id(MyClass._internal)}")
print(f"类属性访问函数id2: {id(MyClass._internal)}")
# 实例访问每次生成新的绑定方法
m1 = obj._internal
m2 = obj._internal
print(f"实例绑定方法id1: {id(m1)}")
print(f"实例绑定方法id2: {id(m2)}")
print(f"两个绑定方法为同一对象: {m1 is m2}")

运行结果会显示类上的函数两次访问id完全一致,而两次获取的实例绑定方法id不同,is判断返回False。如果删除m1、m2的引用触发垃圾回收,再次访问obj._internal时,新生成的绑定方法会复用之前释放的内存地址,与之前观测到的id跳变现象完全吻合。

日常开发中不要依赖实例方法的id值做身份判断、缓存键等逻辑即可,该机制不会影响正常的方法调用功能。


内容的提问来源于stack exchange,提问作者mrkbutty

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.28 13:09:10