三个概念
copy.deepcopy()
深拷贝:将所有变量进行拷贝,对于指针变量进行解地址拷贝(解指针:*var
)copy()或copy.copy()
浅拷贝:将所有变量进行拷贝(无论是普通变量还是指针变量)=
代表引用:跟C++中的int &v = p
效果一样
- Python中的可变数据类型:list, dict, set
- Python中的不可变数据类型(一经创建就不可更改,只能重建一个新的):Number(int, float, …), string, tuple
- 对于不可变数据类型,不存在深浅拷贝的概念
下例对三个概念进行说明
最后再讨论Numpy
封装的数组与其的不同之处
import copy # copy模块中有深拷贝
L = [3.1, 3.2]
L1 = [1, 2, L] # 原始数组
L2 = L1 # 引用
L3 = L1.copy() # 浅拷贝
L4 = copy.deepcopy(L1) # 深拷贝
第一组:改变列表L1
L1[0] = 3 # L2变化,L3和L4不变
print("第一组", L1, L2, L3, L4, sep = '\n')
结果:
第一组
[3, 2, [3.1, 3.2]]
[3, 2, [3.1, 3.2]]
[1, 2, [3.1, 3.2]]
[1, 2, [3.1, 3.2]]
第二组:改变列表L
L1[2][0] = 3.14 # L2和L3变化,L4不变
print("第二组", L1, L2, L3, L4, sep = '\n')
结果:
第二组
[1, 2, [3.14, 3.2]]
[1, 2, [3.14, 3.2]]
[1, 2, [3.14, 3.2]]
[1, 2, [3.1, 3.2]]
第三组:Numpy多维数组
import numpy as np
import copy as cp
arr1 = np.arange(1, 10).reshape(3, 3)
arr2 = arr1 # 引用
arr3 = arr1.copy() # 浅拷贝
arr4 = cp.deepcopy(arr1) # 深拷贝
arr1[0, 0] = 10000
print(arr1, arr2, arr3, arr4, sep="\n")
结果:
[[10000 2 3]
[ 4 5 6]
[ 7 8 9]]
[[10000 2 3]
[ 4 5 6]
[ 7 8 9]]
[[1 2 3]
[4 5 6]
[7 8 9]]
[[1 2 3]
[4 5 6]
[7 8 9]]
结论
1. 对于Python中的可变数据类型,引用变量和原变量共有一个内存,因此其中一个改变,另外一个也跟着改变
2. 对于Python中的不可变数据类型,由于创建后就不能更改,因此其中一个改变等价于重新实例化了,两个变量就不再有关系了。
3. 浅拷贝变量,会连同指针变量一起拷贝,因此指针变量所指的内容改变,浅拷贝内容也会跟着变
4. 深拷贝变量,无论先前拷贝的变量如何变化,跟这个变量的内容都没有关系
5. Numpy
封装的数组底层是由C++
的多维数组实现的,因此没有一个数据单元存储的是指针,所以不存在深浅拷贝问题
补充内容
以下是我在写cs231n
遇到的一个小bug,debug了很久才发现是一个基础问题
例子
class Test:
def __init__(self, a):
# a是一个列表
b = a # 引用
b.append(1)
print("a = {}\nb = {}".format(a, b)) # 很显然a和b会一起更改
if __name__ == "__main__":
s = [1, 2, 3]
test = Test(s)
print("s =", s)
运行结果:
a = [1, 2, 3, 1]
b = [1, 2, 3, 1]
s = [1, 2, 3, 1]
解释:首先创建一个数组s = [1, 2, 3]
,然后在实例化Test
的时候,类初始化中的局部变量a
也引用了s
,然后b = a
再次引用了s
。目前为止,有[1, 2, 3]
有三个变量对其进行了引用,改变其中一个,剩余两个都会发生改变。