python 坑之: 深拷贝和浅拷贝

65 阅读4分钟

最近工作中遇到了深拷贝和浅拷贝的问题,又被绕晕了,于是又开始复习。

python的语法是真烦呀,一层包一层的。还有各种语法糖,不如Go简洁

python 深拷贝和浅拷贝

链接zhuanlan.zhihu.com/p/57893374

浅拷贝的原理

只复制一层,如果对象中还有对象,则只复制对象的引用,而不复制对象本身。

深拷贝的原理

复制所有层,包括对象中的对象,都复制一份新的对象。

浅拷贝和深拷贝的区别

浅拷贝只复制一层,如果对象中还有对象,则只复制对象的引用,而不复制对象本身。 深拷贝复制所有层,包括对象中的对象,都复制一份新的对象。

浅拷贝适用于对象中只有一层的情况,如果对象中有对象,则使用深拷贝。

python 引用计数器

img

list1 = [1, 2, 3, [4, 5]]
list2 = list1
import copy
​
a = "张小鸡"
b = a
c = copy.copy(a)
d = copy.deepcopy(a)
​
print "赋值:id(b)->>>", id(b)
print "浅拷贝:id(c)->>>", id(c)
print "深拷贝:id(d)->>>", id(c)

浅拷贝

img

修改第一层

import copy
​
list1 = [1, 2, 3, [4, 5]]
list2 = copy.copy(list1)
​
print("初始 id(list1[0]):", id(list1[0]))
print("初始 id(list2[0]):", id(list2[0]))  # 一样!
​
list2[0] = 10
print("改后 id(list1[0]):", id(list1[0]))  # 还是 1 的 id
print("改后 id(list2[0]):", id(list2[0]))  # 变成 10 的 id
​
​
print("改后list1:", list1)  # list1 [1, 2, 3, [4, 5]]
print("改后list2:", list2)  # list1 [10, 2, 3, [4, 5]]print("子列表 id 相同:", id(list1[3]) == id(list2[3]))  # True

修改第二层

list2[3][0] = 88
print("改第二层后:")
print("list1:", list1)  # list1 [1, 2, 3, [88, 5]]
print("list2:", list2)  # list1 [10, 2, 3, [88, 5]]

python 赋值和修改对象的区别

我的原始版本

操作,真正发生的事 list2[0] = 10,不是修改对象(int是不可变对象),而是创建新的对象,然后让list2[0]指向新的对象 list1[0] 本身并没有变动

list23 = 20, list2[3]指向的是另一个数组对象[4,5] (这里暂时用sub_list代替), sub_list数组本身的地址是不变的,但是数组内部的第一个元素sub_list[0]发生了变化。 因为int是不可变对象,所以sublist[0]指向了新的地址,地址里面的值88,而不是指向原来的对象4。

所以,虽然sub_list[0]的地址变了,但是sub_list这个数组对象的地址并没有变,所以list2[3]的地址没有变。 list1 和list2的数组都指向了同一个sub_list对象,所以修改第二层元素后,list1 和list2的值都发生了变化。 在表面看来,就是地址不变,但是内部元素发生了变化,所以list1 和list2的值都发生了变化。 实际的情况是:list1 和list2指向sublist的地址不变,但是sublist[0]的地址变了,所以list1 和list2的值都发生了变化。

Grok优化版本

操作真正发生的事
list2[0] = 10不是修改对象(int是不可变对象),而是创建新的对象,然后让 list2[0] 指向新的对象
list2[3][0] = 20list2[3] 指向的是另一个数组对象 [4,5](用 sub_list 代替),
sub_list 数组本身的地址不变,但内部 sub_list[0] 发生了变化。
因为 int 不可变,所以 sub_list[0] 指向了新的地址(值 20),不再指向原来的 4。
虽然 sub_list[0] 的地址变了,但 sub_list 这个数组对象的地址没变,
所以 list1[3]list2[3] 仍然指向同一个 sub_list
→ 两个列表看到的都是 [20, 5]

GLM优化版本

浅拷贝核心机制:引用的复制 copy.copy() 创建一个新容器,但新容器内的元素全部是原容器元素的引用。

  1. 修改第一层 (list2[0] = 10) 机制: 重新赋值。 list2[0] 的引用从对象 1 指向了新对象 10。 结果: list1 和 list2 在顶层完全独立,互不影响。
  2. 修改第二层 (list23 = 88) 机制: 原地修改共享对象。 list1[3] 和 list2[3] 指向同一个内部列表对象 sub_list。此操作修改了该共享对象的内容。 结果: list1 和 list2 的嵌套部分同步变化。