df.copy()能隔离修改是因为它深拷贝了底层numpy数组、行索引和列索引,三者内存地址均不同;但列中嵌套可变对象或直接修改columns.values仍会共享内存,导致意外污染。

因为 copy() 创建的是独立内存对象,原始 DataFrame 和副本之间没有共享数据缓冲区或索引引用。
df.copy() 为什么能隔离修改?
调用 df.copy()(默认 deep=True)时,Pandas 会复制底层 NumPy 数组、索引和列名对象——但注意:列名(df.columns)本身是 Index 对象,copy(deep=True) 会复制它;而如果列中包含可变对象(如嵌套列表),这些内部对象仍可能被共享。
-
df.copy(deep=True)复制数据数组、行索引、列索引,三者地址均与原对象不同(可用id(df.values)、id(df.index)、id(df.columns)验证) - 修改副本的数值列(如
df_copy.loc[0, 'Age'] = 99)只写入副本自己的数组内存,不影响原始数组 - 但如果原始 DataFrame 某列存的是 Python 列表(如
[[1,2], [3]]),即使 deep copy,这些列表对象仍被引用——改列表内容会同步反映到原始 df(这是浅拷贝遗留行为)
为什么 df.columns.values[1] = 'x' 会污染原 df?
这不是 copy() 失效,而是你绕过了 Pandas 的列名管理机制,直接篡改了 columns.values 这个 NumPy 数组的底层内存。而 df.copy(deep=True) 虽复制了 columns 对象,但 columns.values 是一个指向字符串数组的视图,其底层 buffer 在某些版本中未被深拷贝。
- 错误写法:
df2.columns.values[1] = 'new_col'→ 直接写内存,原 df 列名也变 - 正确写法:
df2 = df2.rename(columns={df2.columns[1]: 'new_col'})或df2.columns = df2.columns.tolist()[:1] + ['new_col'] + df2.columns.tolist()[2:] - 极端情况需彻底隔离:用
import copy; df2 = pd.DataFrame(copy.deepcopy(df1.values), columns=copy.deepcopy(df1.columns), index=copy.deepcopy(df1.index))
SettingWithCopyWarning 出现时,其实没在操作副本
这个警告常被误解为“你在改副本”,实际恰恰相反:Pandas 不确定你是在改视图还是副本,而它更怕你**以为自己在改副本,结果却意外改了原数据**(比如布尔索引后赋值)。真正危险的是 df[mask]['col'] = x 这种链式赋值——它可能静默更新原 df,也可能什么都不做。
- 安全写法永远是:
df.loc[mask, 'col'] = x(明确就地写入原 df) - 想改副本?先显式拷贝:
df_sub = df[mask].copy(),再df_sub['col'] = x - 警告不是 bug,是 Pandas 在说:“我没法保证这行代码语义清晰,请你明确选一种意图”
最易忽略的点:列名、索引、数据数组三者的拷贝深度并不总是一致;只要涉及 .values、.array 或直接索引 .columns.values[i],就可能跳出 Pandas 的保护层,触达共享内存。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











