为何函数会原地修改pd.DataFrame?且仅修改至首个.drop前?
为什么函数内变量赋值会改到原DataFrame?
Pandas的DataFrame是引用类型,当你在函数里写df = input_df时,只是让df这个变量指向了和原输入完全相同的内存对象。这时候如果对df做原地修改操作(比如直接修改列值df['col'] = ...、df.fillna(inplace=True)这类带inplace=True的方法),本质就是在修改原对象的内存数据,自然会影响到函数外的原DataFrame。
为什么修改仅停留在首个.drop操作前?
如果你的.drop是用df = df.drop(...)(不带inplace=True)的形式,那问题就出在这:.drop会返回一个全新的DataFrame对象,执行这行代码后,df变量会重新指向这个新对象,和原输入的内存地址彻底断开了。所以在这之后对df的所有操作,都只会作用在新对象上,不会影响原输入。而在这之前的操作,都是在原对象上做的原地修改,所以能被外部看到。
举个直观的例子:
def process_df(input_df): df = input_df # 这一步是原地修改原对象,外部能看到变化 df['new_col'] = 1 # 这一步返回新对象,df指向新内存,和原输入彻底无关 df = df.drop('old_col', axis=1) # 后续操作只改新对象,原输入不受影响 df['another_col'] = 2 return df
这时候原输入的DataFrame会新增new_col,但不会丢失old_col,也不会有another_col——因为drop之后df已经不是原来的那个对象了。
为什么Polars没有这个问题?
Polars的核心设计就是默认不可变,几乎所有操作都会返回一个新的DataFrame对象,不会修改原数据本身。哪怕你在函数里写pl_df = input_pl_df,后续对pl_df做的任何操作(比如.drop)都会生成新对象,原输入的Polars DataFrame始终保持初始状态。只有极少数低层级场景才会涉及原地操作,日常使用根本碰不到,所以不会出现Pandas这种意外修改原数据的情况。
怎么解决Pandas的这个问题?
最简单的办法就是在函数开头先复制原DataFrame,彻底断开引用关系:
def process_df(input_df): # 深拷贝,完全独立于原对象 df = input_df.copy(deep=True) # 后续所有操作都在副本上,不会影响原数据 df['new_col'] = 1 df = df.drop('old_col', axis=1) df['another_col'] = 2 return df
如果你的DataFrame没有嵌套结构(比如没有包含列表、字典的列),用copy(deep=False)(浅拷贝)也能满足需求,速度会更快。
内容的提问来源于stack exchange,提问作者gernophil

