0%

Python - 浅拷贝和深拷贝解析

1. 浅拷贝和深拷贝的区别

浅拷贝(copy):仅拷贝对象,但不拷贝此对象内的子对象。
深拷贝(deepcopy):完全拷贝对象,包括其内部的子对象。

2. 举例理解

1
2
3
4
5
6
7
8
9
10
11
12
13
# -*- coding:utf-8 -*-

import copy

a = {0:1, 1:[1,2]}

a1 = copy.copy(a) # 浅拷贝
a2 = copy.deepcopy(a) # 深拷贝

a[1].append(3)
print(a, '%x' % id(a), '%x' % id(a[1])) # 使用16进制格式化输出id值
print(a1, '%x' % id(a1), '%x' % id(a1[1]))
print(a2, '%x' % id(a2), '%x' % id(a2[1]))
1
2
3
4
输出结果:
{0: 1, 1: [1, 2, 3]} 220a8caf2d0 220aa969e08
{0: 1, 1: [1, 2, 3]} 220a8caf318 220aa969e08
{0: 1, 1: [1, 2]} 220aa937c18 220aa969ec8

从输出结果进行分析:
1.a、a1、a2三者的id值不同。说明不论深浅拷贝都能得到新的对象。
2.a1[1]和a[1]的id值相同,a2[1]和a[1]的id值不同。说明浅拷贝不能拷贝子对象,执行浅拷贝得到的对象a1的子对象和原对象a的子对象使用的是相同的一块内存,但是执行深拷贝得到的对象a2却和原对象a完全独立。这也是为什么当a[1].append(3)后,a1[1]也跟着变了,而a2[1]却没变。


3. a.copy()和copy.copy(a)的区别

虽然两者都是浅拷贝,但是copy.copy()能够拷贝的对象更多,即某些对象本身是没有copy属性的,比如元组(tuple)。
如果将上例中的a值替换为a = (1, [1,2]),那么执行a1 = a.copy()时是会报错的:

1
2
3
4
# -*- coding:utf-8 -*-

a = (1, [1,2])
a1 = a.copy()
1
AttributeError: 'tuple' object has no attribute 'copy'

4. 引申: 手写字典深拷贝

1
2
3
4
5
6
7
8
9
def deep_copy(x):
'''仅适用于子对象是字典的情况'''
x1 = {}
for k, v in x.items():
if isinstance(v, dict): # 当子对象是字典类型时递归执行
x1[k] = deep_copy(v)
else:
x1[k] = v
return x1

如果子对象是数组或者元组的话,只要在赋值前价格数据类型判断,然后根据不同的类型赋值给不同的变量即可兼容。