在阐述引用、浅拷贝和深拷贝前,首先需要要了解 Python 的世界里,一切皆对象,每个对象各包含一个 idendity、type 和 value。如下图所示,id 、type 均为不可变的;value即可为可变的,也可为不可变的。
1、引用 (Reference)
>>> b = [1 , 2]
>>> a = [b, 3, 4]
>>> c = a # c = a 表示 c 和 a 指向相同的地址空间,并没有创建新的对象。
>>> print(c)
[[1, 2], 3, 4]
>>> id(a)
4408148552
>>> id(c)
4408148552
2、浅拷贝(Shallow Copy)
d = copy.copy(a) 是浅拷贝~~~虽然它有两个copy...等价于 d = a.copy()
浅拷贝只会拷贝父对象,不会拷贝父对象中的子对象,所以若a的子对象变,则d变;但是父对象变,d不会改变。
>>> import copy
>>> d = copy.copy(a) # 创建了一个新对象,复制了原有对象的引用
>>> print(d)
[[1, 2], 3, 4]
>>>
>>> id(d) # d 和 a 的地址不一样
4408199792
>>> id(a)
4408148552
>>>
>>> id(a[0]) # a[0] 和 d[0]地址一样,即子对象地址一样
4408022944
>>> id(d[0])
4408022944
>>> d[0][0] = 5 #子对象改变,则两者都改变
>>> print(d)
[[5, 2], 3, 4]
>>> print(a) # 由于a[0] 和 d[0]地址一样,则改变 d[0][0]的值,对a[0][0]有影响。
[[5, 2], 3, 4]
>>>
>>>id(a[1]) # a[1] 和 d[1]地址不一样
1997107680
>>>id(d[1])
1997106720
>>> a[1] = 33 # 父对象的改变,d不会改变
>>> print(a)
[[5, 2], 33, 4]
>>> print(d) # 由于a[1] 和 d[1]地址不一样,则改变 a[1]的值,对 d[1]无影响。
[[5, 2], 3, 4]
3、深拷贝(Deep Copy)
深拷贝创建一个新对象,对于对象中的元素,深拷贝都会重新生成一份,而不是简单的使用原始元素的引用。
# e = copy.deepcopy(a) 新建了一个新对象,完整的在内存中复制原有对象
>>> e = copy.deepcopy(a)
>>> print(e)
[[1, 2], 3, 4]
>>> id(a) # e 和 a 的地址不一样
4408148552
>>> id(e)
4408394792
>>> id(a[0]) # e[0] 和 a[0] 的地址不一样
4408022944
>>> id(e[0])
4408398432
>>> e[0][0] = 5
>>> print(e) # 由于e[0] 和 a[0] 的地址不一样,则改变 e[0] 的值不会改变 a[0] 的值
[[5, 2], 3, 4]
>>> print(a)
[[1, 2], 3, 4]
Note
关于浅拷贝和深拷贝的区别,Python 的 document是这样解释的:
The difference between shallow and deep copying is only relevant for compound objects (复合对象)(objects that contain other objects, like lists or class instances):
- A shallow copy constructs a new compound object and then (to the extent possible) inserts references into it to the objects found in the original.
- A deep copy constructs a new compound object and then, recursively(递归地), inserts copies into it of the objects found in the original.