为何Pandas DataFrame单元格的id会随每次执行变化?
理解Pandas视图中元素ID变化的原因
首先明确两个核心事实:
- 你定义的
df1 = df.iloc[:3, :]确实是df的视图,二者共享底层的numpy数组内存,修改df1的元素会直接影响df,这一点没有问题。 - 你看到的ID变化和看似矛盾的相等判断,本质上是Python的
id()函数特性 + Pandas访问单个元素的实现逻辑共同导致的,和视图本身的内存共享无关。
1. 为什么每次访问元素的ID会变化?
当你通过df.loc[0, 'a']或df1.loc[0, 'a']获取单个元素时,Pandas并不会直接返回底层数组中对应内存位置的引用。相反,它会创建一个新的标量对象(这里是numpy.int64类型,因为你的数据来自np.arange)来包装这个值。
每次执行df.loc[0, 'a'],都会生成一个全新的标量实例,这个实例的内存地址(也就是id()返回的值)自然会不同——除非前一个实例已经被Python的垃圾回收器回收,新实例刚好复用了同一个内存地址(这就是你第一次执行时ID相同的原因)。
2. 为什么偶尔打印的ID不等,但id(...) == id(...)返回True?
这是Python中id()函数的一个常见“陷阱”:id()返回的是对象在其生命周期内的内存地址。当你在同一行执行id(df.loc[0, 'a']) == id(df1.loc[0, 'a'])时:
- 第一个
df.loc[0, 'a']创建的标量对象,在id()调用完成后就没有任何引用了,会被立即标记为可回收。 - 紧接着创建的
df1.loc[0, 'a']标量对象,可能刚好被分配到了刚刚释放的内存地址上,所以两者的id()相等,判断返回True。
但如果你分开两行打印,中间可能有其他操作(哪怕只是Python解释器的内部操作),导致第一个对象的内存地址被其他对象占用,第二个对象只能用新的地址,所以打印出的ID就不一样了。
3. 验证视图共享内存的正确方式
如果要确认df和df1确实共享底层内存,不要通过单个元素的id()来判断,而是直接修改视图的元素,看原DataFrame是否变化:
df1.loc[0, 'a'] = 999 print(df.loc[0, 'a']) # 会输出999,说明底层内存共享
或者直接查看底层数组的内存地址:
print(id(df.values)) print(id(df1.values)) # 两者ID相同,说明共享同一个numpy数组
总结一下:你看到的ID变化是因为每次访问元素都生成了新的包装对象,而视图本身的底层内存是完全共享的——这是Pandas的设计逻辑(方便标量操作)和Python内存管理机制共同作用的结果。
内容的提问来源于stack exchange,提问作者Y. Gao
相关产品推荐
相关产品推荐

