学习python的第十一天之数据类型——拷贝之浅拷贝和深拷贝
学习python的第十一天之数据类型——拷贝之浅拷贝和深拷贝
浅拷贝(Shallow Copy)
浅拷贝创建一个新的对象,但不复制嵌套的对象。原对象中的嵌套对象引用会被保持在新对象中,这意味着新对象和旧对象共享嵌套的对象。
特点:
- 新对象和旧对象是独立的,但它们内部的引用指向相同的对象。
- 修改新对象中的嵌套对象也会影响旧对象中的嵌套对象,因为它们是共享的。
实现方式:
- 使用
copy
模块中的copy
函数。- 使用某些内置方法,如列表的
.copy()
方法。
import copy
list1 = ['张三', 25, '男', [1, 3.14, '您好']]
list2 = list1.copy()
list3 = copy.copy(list1)
print(list1, id(list1)) # 输出: ['张三', 25, '男', [1, 3.14, '您好']] 1479882705920
print(list2, id(list2)) # 输出: ['张三', 25, '男', [1, 3.14, '您好']] 1479882706432
print(list3, id(list3)) # 输出: ['张三', 25, '男', [1, 3.14, '您好']] 1479882707392
for e1 in list1:
print(e1, id(e1))
# 张三 1479913920624
# 25 1479877618736
# 男 1479914043552
# [1, 3.14, '您好'] 1479914011008
for e2 in list2:
print(e2, id(e2))
# 张三 1479913920624
# 25 1479877618736
# 男 1479914043552
# [1, 3.14, '您好'] 1479914011008
for e3 in list3:
print(e3, id(e3))
# 张三 1479913920624
# 25 1479877618736
# 男 1479914043552
# [1, 3.14, '您好'] 1479914011008
list1[0] = '李四'
print(list1, id(list1)) # 输出: ['李四', 25, '男', [1, 3.14, '您好']] 1479882705920
print(list2, id(list2)) # 输出: ['张三', 25, '男', [1, 3.14, '您好']] 1479882706432
print(list3, id(list3)) # 输出: ['张三', 25, '男', [1, 3.14, '您好']] 1479882707392
# 当只是修改浅层数据的时候,list2和list3的内容不会随着list1的内容变动而变动,
# 且list1、list2、list3的地址也不会变化
import copy
list1 = ['张三', 25, '男', [1, 3.14, '您好']]
list2 = list1.copy()
list3 = copy.copy(list1)
print(list1, id(list1)) # 输出: ['张三', 25, '男', [1, 3.14, '您好']] 1719205170176
print(list2, id(list2)) # 输出: ['张三', 25, '男', [1, 3.14, '您好']] 1719205170688
print(list3, id(list3)) # 输出: ['张三', 25, '男', [1, 3.14, '您好']] 1719205171648
print(list2[3], id(list2[3])) # 输出: [1, 3.14, '您好'] 1719236487872
print(list2[3][2], id(list2[3][2])) # 输出: 您好 1719236450512
list2[3][2] = 'HELLO'
print(list1, id(list1)) # 输出: ['张三', 25, '男', [1, 3.14, 'HELLO']] 1719205170176
print(list2, id(list2)) # 输出: ['张三', 25, '男', [1, 3.14, 'HELLO']] 1719205170688
print(list3, id(list3)) # 输出: ['张三', 25, '男', [1, 3.14, 'HELLO']] 1719205171648
print(list2[3], id(list2[3])) # 输出: [1, 3.14, 'HELLO'] 1719236487872
print(list2[3][2], id(list2[3][2])) # 输出: HELLO 1719236513136
# 当修改深层次的数据时,因为列表的存储内容归根结底是每个元素的地址,
# 所以list1[3]存储的其实是[1, 3.14, '您好']的地址,
# 同理list2和list3也是一样,
# 前面说了,修改列表不会引起地址的改变,
# 所以将[1, 3.14, '您好']中的'您好'替换成'HELLO'后,[1, 3.14, 'HELLO']的地址不变,
# 所有list1[3]、list2[3]、list3[3]存储的地址所指引的内容就变成了[1, 3.14, 'HELLO'],
# 这也就是为什么只是修改了list2[3]中的一个元素,list1和list3都发生改变了;
# 另外为什么'您好'改为'HELLO'后,'您好'的地址和'HELLO'的地址发生了改变:
# 因为str字符串类型是不可变类型,内容发生改变后地址就会改变;
# 而list列表类型是可变类型,所以[1, 3.14, '您好']和[1, 3.14, 'HELLO']的地址没有发生改变;
深拷贝(Deep Copy)
深拷贝创建一个新的对象,并递归地复制所有嵌套的对象。这意味着新对象和旧对象在所有层级上都是独立的,修改新对象不会影响旧对象。
特点:
- 新对象和旧对象是完全独立的,它们内部的引用指向不同的对象。
- 修改新对象中的任何对象都不会影响旧对象。
实现方式:
- 使用
copy
模块中的deepcopy
函数。
import copy
list1 = ['张三', 25, '男', [1, 3.14, '您好']]
list2 = copy.deepcopy(list1)
print(list1, id(list1)) # 输出: ['张三', 25, '男', [1, 3.14, '您好']] 2827084499840
print(list2, id(list2)) # 输出: ['张三', 25, '男', [1, 3.14, '您好']] 2827084500352
for e1 in list1:
print(e1, id(e1))
# 张三 2827115649136
# 25 2827075808304
# 男 2827115783472
# [1, 3.14, '您好'] 2827115751872
for e2 in list2:
print(e2, id(e2))
# 张三 2827115649136
# 25 2827075808304
# 男 2827115783472
# [1, 3.14, '您好'] 2827084501312
# 从[1, 3.14, '您好']的地址可以看出,深拷贝的列表中可变类型元素,会有一个新地址,而不是像浅拷贝一样共用一个地址;
list2[0] = '李四'
print(list1, id(list1)) # 输出: ['张三', 25, '男', [1, 3.14, '您好']] 2827084499840
print(list2, id(list2)) # 输出: ['李四', 25, '男', [1, 3.14, '您好']] 2827084500352
list2[3][2] = 'HELLO'
for e1 in list1:
print(e1, id(e1))
# 张三 2827115649136
# 25 2827075808304
# 男 2827115783472
# [1, 3.14, '您好'] 2827115751872
for e2 in list2:
print(e2, id(e2))
# 李四 2827115710288
# 25 2827075808304
# 男 2827115783472
# [1, 3.14, 'HELLO'] 2827084501312
总结
- 浅拷贝:复制了对象本身,但对象中的嵌套对象仍然是共享的。
- 深拷贝:递归地复制了对象及其所有嵌套对象,使得新对象和旧对象完全独立。
选择浅拷贝还是深拷贝取决于你的具体需求。如果你只需要一个新的容器但里面的元素可以共享,浅拷贝是更高效的。如果你需要完全独立的对象副本,深拷贝是必须的。