Python的引用计数
首先我们要知道,Python 内不可变对象的内存管理方式是引用计数。因此,我们在谈论浅拷贝时,其实谈论的主要特点都是基于可变对象的。
1 | import copy |
结果
1 | 4310112880 |
因为我们这里操作的是不可变对象,Python 用引用计数的方式管理它们,所以 Python 不会对值相同的不可变对象,申请单独的内存空间。只会记录它的引用次数
操作不可变对象时,由于引用计数的特性,浅拷贝和深拷贝是没有区别的
浅拷贝(Shallow Copy)
我们先来比较一下浅拷贝和赋值在可变对象上的区别
1 | import copy |
结果
1 | 4372413312 |
赋值
就是对物体进行贴标签的操作,作用于同一个物体
浅拷贝
则会创建一个新的对象,至于对象中的元素,它依然会引用原来的物体,请看下面的例子
1 | import copy |
结果
1 | 4337646464 |
由于浅拷贝会使用原始元素的引用(内存地址)。所以在操作被拷贝对象内部可变元素时,其结果是会影响到拷贝对象的
现在,我们看下面的例子
1 | import copy |
深拷贝(Deep Copy)
深拷贝遇到可变对象,则又会进行一层对象创建,所以你操作被拷贝对象内部的可变对象,不影响对象内部的值
1 | import copy |
结果
1 | a的值为: [['aaa'], 'bbb'] |
总结
- 由于Python内部引用计数器的特性,对于不可变对象,浅拷贝和深拷贝的作用是一样的,指针指向同一个地址
- 浅拷贝的拷贝,其实是拷贝了原始元素的引用(内存地址),所以当拷贝可变对象时,原对象内可变对象的对应元素的改变,会在复制对象的对应元素上,有所体现
- 深拷贝遇到可变对象时,又在内部新建了一个副本。所以,不管它内部的元素如何变化,都不会影响到原来副本的可变对象
实际应用场景
那么讲了这么多,他们的实际应用场景又是什么呢?这也是网上讲的最少的部分了
避免修改原始数据:当你需要修改一个对象,但又不想影响原始数据时,深拷贝是一个很好的选择。
1
2
3
4
5
6
7
8import copy
original_dict = {'a': 1, 'b': [2,3]}
deep_copy = copy.deepcopy(original_dict)
deep_copy['b'][0] = 4
print(original_dict)
print(deep_copy)结果
1
2{'a': 1, 'b': [2, 3]}
{'a': 1, 'b': [4, 3]}配置文件的处理:当你从配置文件中读取数据并希望对其进行修改,但同时又希望保留原始配置文件的内容时,深拷贝非常有用
多线程/多进程中数据安全:在多线程或多进程环境中,你可能需要确保一个对象在多线程或多进程之间是独立的。通过深拷贝,你可以为每个线程或进程提过一个独立的对象副本
注意:深拷贝可能会消耗更多的内存和时间,因为它需要递归的复制对象的所有内容。因此,在选择使用深拷贝还是浅拷贝时,需要权衡你具体需求(如性能、内存使用等)和场景