最近工作中遇到了深拷贝和浅拷贝的问题,又被绕晕了,于是又开始复习。
python的语法是真烦呀,一层包一层的。还有各种语法糖,不如Go简洁
python 深拷贝和浅拷贝
浅拷贝的原理
只复制一层,如果对象中还有对象,则只复制对象的引用,而不复制对象本身。
深拷贝的原理
复制所有层,包括对象中的对象,都复制一份新的对象。
浅拷贝和深拷贝的区别
浅拷贝只复制一层,如果对象中还有对象,则只复制对象的引用,而不复制对象本身。 深拷贝复制所有层,包括对象中的对象,都复制一份新的对象。
浅拷贝适用于对象中只有一层的情况,如果对象中有对象,则使用深拷贝。
python 引用计数器
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)
浅拷贝
修改第一层
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] = 20 | list2[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() 创建一个新容器,但新容器内的元素全部是原容器元素的引用。
- 修改第一层 (list2[0] = 10) 机制: 重新赋值。 list2[0] 的引用从对象 1 指向了新对象 10。 结果: list1 和 list2 在顶层完全独立,互不影响。
- 修改第二层 (list23 = 88) 机制: 原地修改共享对象。 list1[3] 和 list2[3] 指向同一个内部列表对象 sub_list。此操作修改了该共享对象的内容。 结果: list1 和 list2 的嵌套部分同步变化。