python深浅拷贝

1.浅拷贝

浅拷贝会创建一个新的对象,但是如果原对象中的元素是可变的,那么新对象中的这些元素将是原对象中对应元素的引用(即内存地址相同),而不是它们的拷贝。这意味着,如果你修改了新对象中的这些可变元素,原对象中的相应元素也会被修改。

浅拷贝在需要快速复制对象但不需要完全独立于原对象(特别是当内部元素不可变时)时非常有用。然而,当内部元素是可变类型时,需要特别小心,因为对浅拷贝的修改可能会意外地影响到原对象。

1.1 浅拷贝的实现

在Python中,可以使用copy模块中的copy()函数来实现浅拷贝。

import copy  
  
# 定义一个包含可变元素的列表  
original_list = [1, 2, [3, 4], 5]  
  
# 使用浅拷贝创建新列表  
shallow_copied_list = copy.copy(original_list)  
  
# 检查原始列表和浅拷贝列表的外部元素(非可变元素)是否独立  
print(id(original_list) == id(shallow_copied_list))  # False,表明是两个不同的对象  
  
# 检查原始列表和浅拷贝列表中可变元素(列表[3, 4])的引用是否相同  
print(id(original_list[2]) == id(shallow_copied_list[2]))  # True,表明它们是同一个对象  
  
# 修改浅拷贝列表中的可变元素  
shallow_copied_list[2].append(6)  
  
# 检查原始列表是否也被修改了  
print(original_list)  # 输出 [1, 2, [3, 4, 6], 5],表明原始列表也被修改了

运行结果:

1.2 浅拷贝三种形式

(1)切片操作

如:

lst = [1, 2, [3, 4]]

切片操作:

lst1 = lst[:] 

或者

lst1 = [each for each in lst]

说明:[:]它与[0:]相似,意思是从0索引拆分到末尾。它返回一个新列表。

(2)工厂函数:

lst1 = list(lst)

(3)copy函数:

lst1 = copy.copy(lst)

浅复制要分两种情况进行讨论:

(1)当浅复制的值是不可变对象(字符串、元组、数值类型)时和“赋值”的情况一样,对象的id值(id()函数用于获取对象的内存地址)与浅复制原来的值相同。

(2)当浅复制的值是可变对象(列表、字典、集合)时会产生一个“不是那么独立的对象”存在。有两种情况:

        第一种情况:复制的对象中无复杂子对象,原来值的改变并不会影响浅复制的值,同时浅复制的值改变也并不会影响原来的值。原来值的id值与浅复制原来的值不同。

        第二种情况:复制的对象中有复杂子对象(例如列表中的一个子元素是一个列表),如果不改变其中复杂子对象,浅复制的值改变并不会影响原来的值。但是改变原来的值中的复杂子对象的值会影响浅复制的值。

2. 深拷贝

深拷贝在Python中是指创建一个新的对象,并递归地复制原始对象及其所有子对象(包括嵌套的对象)的所有内容,使得新对象与原始对象完全独立,不共享内存地址。因此,对深拷贝后的对象进行修改,不会影响到原始对象。

深拷贝是Python中处理复杂对象拷贝的一种重要方式,它可以帮助我们创建完全独立于原始对象的新对象,从而避免在修改新对象时影响到原始对象。然而,在使用深拷贝时,我们也需要注意其可能带来的性能问题和循环引用问题

1.1 深拷贝的实现

(1) 使用copy模块的deepcopy()函数

这是最常用的实现深拷贝的方法。copy模块是Python标准库的一部分,提供了deepcopy()函数用于深拷贝操作。

import copy  

original_list = [[1, 2], [3, 4]]  
deep_copied_list = copy.deepcopy(original_list)  

# 修改深拷贝后的列表  
deep_copied_list[0].append(5)  

# 打印原始列表和深拷贝后的列表,查看是否相互独立  
print(original_list)  # 输出: [[1, 2], [3, 4]]  
print(deep_copied_list)  # 输出: [[1, 2, 5], [3, 4]]

(2) 使用pickle模块的loads()dumps()函数:

pickle模块可以将Python对象序列化为字节流,也可以将字节流反序列化为Python对象。通过先序列化再反序列化的过程,可以实现深拷贝。

import pickle  

original_list = [[1, 2], [3, 4]]  
serialized_data = pickle.dumps(original_list)  
deep_copied_list = pickle.loads(serialized_data)  

# 修改深拷贝后的列表  
deep_copied_list[0].append(5)  

# 打印原始列表和深拷贝后的列表,查看是否相互独立  
print(original_list)  # 输出: [[1, 2], [3, 4]]  
print(deep_copied_list)  # 输出: [[1, 2, 5], [3, 4]]

3. 区别归纳

浅拷贝深拷贝
定义重新分配内存,创建新对象,但内部元素是原对象中子对象的引用重新分配内存,创建新对象,并递归拷贝原对象中的所有元素到新对象中
引用类型处理拷贝引用,不拷贝引用的对象拷贝引用的对象,创建新的对象
内存独立性引用类型属性共享内存空间完全独立的内存空间
影响对新对象中引用类型属性的修改会影响原对象对新对象的修改不会影响原对象

在需要完全独立复制对象,且对原对象无影响时使用深拷贝。

在不需要完全独立复制对象,或对象较小且只包含基本数据类型时,可以使用浅拷贝以提高效率。

  • 14
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值