为什么Python列表等对象在函数内索引修改时会改变外部原值
Python函数内修改列表/数组同步影响外部原对象的原因与解决方案
核心原因解释
你的认知偏差核心是没有区分Python中赋值操作和可变对象原地修改操作的差异,以及Python变量本质是对象引用的底层逻辑:
- 所有Python变量都是指向内存中对象的引用,不是值本身。对象分为两类:
- 不可变对象:int、float、str、元组等,无法修改对象本身的内容,只要修改就会生成新的对象
- 可变对象:列表、字典、numpy数组等,支持直接修改对象本身的内容,不需要生成新对象
- 你的第一个示例中,函数内部执行
i=1属于局部赋值操作,相当于在函数局部作用域新建了一个名为i的局部变量,和全局的i是完全独立的两个引用,自然不会修改全局i的值。 - 你的第二个示例中,函数内部执行
img[0] = img[0] + 100属于可变对象的原地修改操作:
函数内部没有定义名为img的局部变量,所以会直接使用全局的img引用,这个引用指向内存中存储的列表对象,通过索引赋值是直接修改该内存对象的内容,因此全局的img读取到的就是修改后的结果。
避免原对象被修改的方案
要防止函数内操作影响外部原可变对象,只需要在操作前对可变对象做拷贝,基于拷贝后的本地副本操作即可,具体方案如下:
- 单层结构的可变对象(比如一维列表、一维numpy数组)用浅拷贝即可:
列表可以用list.copy()方法或者切片list[:],numpy数组可以用.copy()方法 - 嵌套结构的可变对象(比如二维列表、嵌套字典)需要用深拷贝,导入
copy模块调用copy.deepcopy()方法即可
正确示例代码
import copy # 建议将可变对象作为参数传入,不要直接调用全局变量,逻辑更易维护 def fun(input_img): # 拷贝生成本地副本,后续所有操作仅修改副本 local_img = copy.deepcopy(input_img) local_img[0] = local_img[0] + 100 return 'done', local_img img = [0, 1] status, modified_img = fun(img) print(img == [0, 1]) # 输出True,原对象未被修改
内容的提问来源于stack exchange,提问作者Alex Witsil
相关产品推荐
相关产品推荐

