总结
- 直接赋值:其实就是对象的引用。
- 浅拷贝(copy):拷贝父对象,不会拷贝对象的内部的子对象(采用同一引用)。
- 深拷贝(deepcopy):copy 模块的 deepcopy 方法,完全拷贝了父对象及其子对象。
图可以参考:python列表复制(浅拷贝and深拷贝)理解!!!!
引用
在python中,每个对象都会在内存中申请开辟一块空间,使用变量名引用该空间地址。
- 可变对象:允许其在引用空间不变的情况下修改对象的值。如,列表、字典、集合
- 不可变对象:具有固定值的对象,若要改变其值,必须重新创建一个对象引用。如,数值型、布尔型、字符串、元组
可使用id(object)获取对象的内存地址,函数返回对象的唯一标识符,标识符是一个整数。
为了简化内存管理,Python通过引用计数机制实现自动垃圾回收功能,Python中的每个对象都有一个引用计数,用来计数该对象在不同场所分别被引用了多少次。每当引用一次Python对象,相应的引用计数就增1,每当消毁一次Python对象,则相应的引用就减1,只有当引用计数为零时,才真正从内存中删除Python对象。
赋值
Python参数传递采用的是“传对象引用”的方式。即,将一个对象的地址赋值给一个变量,使得变量指向该内存地址。
不可变对象赋值:原变量改变,新变量不受影响。虽然原变量和新变量都指向同一对象,但由于是不可变对象,若要改变原变量,则需创建一个新的对象引用,所以另一变量不受影响。
>>> a = 5
>>> b = a
>>> b
5
>>> id(a) == id(b)
True
>>> a = 6 ### 改变a,此时a的引用对象也改变了
>>> b ### 并不会改变b的引用
5
>>> id(a) == id(b) ### a和b的引用不同了
False
不可变对象赋值:原变量在其引用上改变,新变量同时改变,因为指向的是同一片空间地址。
>>> a = [1,2,3]
>>> b = a
>>> id(a) == id(b)
True
>>> a[0] = 2 ### 改变a
>>> a
[2, 2, 3]
>>> b ### b也跟着变
[2, 2, 3]
>>> id(a) == id(b) ### 引用同一个对象
True
>>> a = [1,2,3]
>>> b = a
>>> id(a) == id(b)
True
>>> id(a)
140382349680464
>>> id(b)
140382349680464
>>> a = [1,2,3] ### 给a重新创建一个对象引用
>>> id(a) ### 注意虽然对象的值和原来一样,但已经是不同的地址空间了
140382349697664
>>> id(a) == id(b)
False
>>> id(b)
140382349680464
浅拷贝
浅拷贝会创建一个新的容器对象,但对于对象中的元素,只会使用原始元素的引用。即外层地址变化,内层元素地址不变。
常见的浅拷贝方法:
使用切片操作[:]
使用工厂函数(如list/dict/set)
copy模块的copy()方法
>>> a = [[1,2], [3,4]]
>>> b = a[:] ## 浅拷贝
>>> id(a) == id(b) ## 外层引用不同
False
>>> id(a[0]) == id(b[0]) ## 内层元素引用相同
True
>>> a.append(5)
>>> a ## 修改a外层,b不变
[[1, 2], [3, 4], 5]
>>> b
[[1, 2], [3, 4]]
>>> a[0].append(3) ## 修改a内层可变对象,b也变
>>> a
[[1, 2, 3], [3, 4], 5]
>>> b
[[1, 2, 3], [3, 4]]
深拷贝
深拷贝除创建一个新容器对象外,对于内层的各元素,也重新创建一个对象引用,因此不管原变量怎么变,新变量都不变。
深拷贝方法:
使用 copy 模块的 deepcopy 方法
>>> a = [[1,2], [3,4]]
>>> b = copy.deepcopy(a)
>>> id(a) == id(b)
False
>>> id(a[0]) == id(b[0]) ## 内层各元素引用也不同
False
>>> a[0].append(3) ## 不管a的元素怎么变,b都不变
>>> a
[[1, 2, 3], [3, 4]]
>>> b
[[1, 2], [3, 4]]
以上都是对于可变对象的归纳,对不可变对象,复制/浅拷贝/深拷贝都是引用原对象的内存地址。但无论原对象发生什么变化,赋值/浅拷贝/深拷贝都不会跟着变。
注意,如果是不可变对象的值相同,那么引用的一定是同一片地址
>>> a = [[1,2], [3,4]]
>>> b = copy.deepcopy(a)
>>> id(a[0]) == id(b[0])
False
>>> id(a[0][0]) == id(b[0][0]) ## 不可变对象值都为1,同一引用
True