Python使用*扩展列表后deepcopy未消除内部引用原因咨询
问题核心原因
出现这个现象本质是两个Python机制共同作用的结果,和deepcopy本身的功能失效无关:
- 首先是列表
*运算符的复制逻辑:对列表执行*n扩展时,只会复制列表结构,不会递归创建内部元素的新副本,仅复制内部元素的引用。
你代码里l1*3生成的临时列表,长度是6,但内部实际只引用了2个字典对象:第0、2、4位的三个引用全部指向l1里原有的{'a':1}字典,第1、3、5位的三个引用全部指向l1里原有的{'b':2}字典,不存在6个独立字典。 - 其次是
copy.deepcopy的缓存设计:deepcopy执行时会维护一个memo缓存字典,记录所有已经完成复制的原对象和对应新副本的映射,这个设计原本是为了处理循环引用、避免无限递归。当deepcopy遍历到同一个原对象的重复引用时,不会重复创建新副本,会直接返回缓存里已经生成的对应副本。
代码实际执行流程
当你调用copy.deepcopy(l1*3)时,实际执行逻辑是:
- 遍历到第一个元素(原
{'a':1}对象):缓存无记录,创建一个新的{'a':1}副本存入缓存,放到l2第0位 - 遍历到第二个元素(原
{'b':2}对象):缓存无记录,创建一个新的{'b':2}副本存入缓存,放到l2第1位 - 遍历到第三个元素(和第0位是同一个原
{'a':1}对象):查缓存发现已经复制过,直接把之前生成的{'a':1}副本的引用放到l2第2位,不新建对象 - 遍历到第四个元素(和第1位是同一个原
{'b':2}对象):同理直接返回缓存里的{'b':2}副本引用放到l2第3位 - 后续第4、5位元素重复上述逻辑,最终l2里的3个
{'a':xxx}全部指向deepcopy生成的同一个字典副本,3个{'b':xxx}也全部指向另一个同一个字典副本。
这时候你修改l2[0]['a'] =7,本质是修改三个位置共同指向的那一个字典对象,自然所有对应位置的值都会同步变化。deepcopy本身正常完成了深拷贝工作,只是它忠实保留了原列表里「多个位置引用同一个对象」的关系,没有额外把重复引用拆成独立对象。
方案说明
- 你找到的列表推导式方案
l2 = [d.copy() for d in l1*3]能生效,是因为遍历过程中每遇到一个元素(哪怕是重复引用的同一个原字典),都会手动执行一次字典浅拷贝生成新对象,最终每个位置的字典都是独立的。注意这个方案是浅拷贝,如果字典内部还有嵌套的可变对象,依然会存在引用共用问题。 - 如果需要彻底规避引用问题、支持任意嵌套结构的可变元素,更稳妥的写法是在生成列表时就为每个位置创建独立的深拷贝副本,不要依赖
*做元素扩展:
import copy l1 = [{'a':1},{'b':2}] l2 = [copy.deepcopy(d) for _ in range(3) for d in l1]
内容的提问来源于stack exchange,提问作者SFA
相关产品推荐
相关产品推荐

