Python传列表到函数append时修改原列表的原因及解决方法
结论
该现象是Python的完全正常的预期行为,不属于bug,核心由Python的参数传递规则和可变/不可变对象的特性决定。
原理说明
Python的参数传递采用传对象引用(也称为传赋值)的规则:
- 所有变量存储的都是指向内存中实际对象的引用,变量本身没有类型,类型归属于它指向的对象。
- 调用函数传参时,本质是把实参持有的对象引用赋值给函数的形参,形参和实参最初指向内存中的同一个对象,不会自动生成对象的拷贝。
你观察到的修改结果差异,来自操作对象的类型和操作方式不同:
- 列表、自定义类实例属于可变对象:调用
append()、修改实例属性这类操作属于原地修改,直接改动了引用指向的对象本身,由于形参和实参指向同一个对象,函数内外都会观测到改动。 - 整数、字符串、元组属于不可变对象:不存在原地修改的方法,你写的
variable += 1本质是创建了一个值为variable+1的新整数对象,再让函数内部的形参指向这个新对象,外部的实参仍然指向原来的整数对象,因此外部观测不到变化。
规避方法
根据业务场景选择对应方案即可:
- 传入参数时主动传递副本
如果不希望函数内部修改影响外部原始数据,传参时传入对象的副本即可。普通一维列表可以用浅拷贝生成独立副本:
对于自定义类实例的场景,如果不希望修改原实例的属性,也可以在传参前生成实例的拷贝,或者在函数内部操作属性的副本而非原属性。# 以下三种写法都可以生成列表的浅拷贝 mc.main_function_list(list_.copy()) mc.main_function_list(list_[:]) mc.main_function_list(list(list_)) - 函数内部避免原地修改,返回新对象
尽量不在函数内部对传入的可变对象做原地修改操作,而是基于传入值生成新对象返回,由调用方自行决定是否用返回值替换原数据:def main_function_list(self,variable): # 拼接生成新列表,不改动传入的原列表 return variable + [1] - 嵌套可变对象使用深拷贝
如果传入的可变对象内部还嵌套了其他可变对象(比如列表套列表、列表存自定义实例),浅拷贝只会复制最外层结构,内层元素仍然是共享引用,此时需要用标准库copy的deepcopy方法生成完全独立的完整副本:import copy mc.main_function_list(copy.deepcopy(list_))
内容的提问来源于stack exchange,提问作者markk borras
相关产品推荐
相关产品推荐

