GeophyAI

Python与地球物理数据处理

0%

Python之深copy与浅copy

C和Python中简单变量的拷贝问题

所谓浅拷贝,即变量之间的差异只体现在声明的名称上,他们实质上指向同一块内存地址;而深拷贝则是内存地址完全不相同的两个变量。
在C中(包括C++,CUDA),两个值相同的变量内存地址是不同的,而在python中则不太一样,下面我们通过几个例子了解一下python中浅拷贝和深拷贝问题。

C

用代码说话。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
int a = 1;
int b = 1;
printf("addr of a = %p\n", &a);
printf("addr of b = %p\n", &b);
a = 2;
printf("addr of a = %p\n", &a);
printf("addr of b = %p\n", &b);
b = 2;
printf("addr of a = %p\n", &a);
printf("addr of b = %p\n", &b);
[usr]./test_shallowcopy_ofC.out
addr of a = 0x7ffdb8b0344c
addr of b = 0x7ffdb8b03448
addr of a = 0x7ffdb8b0344c
addr of b = 0x7ffdb8b03448
addr of a = 0x7ffdb8b0344c
addr of b = 0x7ffdb8b03448
// 整个过程中a,b的地址并没有发生改变

Python

简单变量

用代码说话。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# copypy.py
a = 1
b = 1
print("addr_of_a=",hex(id(a))) # 输出a的16进制地址
print("addr_of_b=",hex(id(b)))
a = 2
print("addr_of_a=",hex(id(a)))
print("addr_of_b=",hex(id(b)))
b = 2
print("addr_of_a=",hex(id(a)))
print("addr_of_b=",hex(id(b)))
>>> python copypy.py
addr_of_a= 0x563c06329320
addr_of_b= 0x563c06329320
# 当初始化的两个变量值相同时,其地址相同
addr_of_a= 0x563c06329340
addr_of_b= 0x563c06329320
# 改变某一个变量后其地址也会发生改变
addr_of_a= 0x563c06329340
addr_of_b= 0x563c06329340
# 将两个变量改变为相同的值,他们的地址也会变为相同

列表

所以,当使用直接赋值的方式b = a进行变量值的传递时可能会出现问题,当两个变量只是简单的两个数值时似乎并没有什么不同,我们再看以下例子。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# test_copylist.py
a = [True, [True, True], True]
b = a
print('a=',a)
print('b=',b)
a[0] = False
a[1][0] = [False]
print('a=',a)
print('b=',b)
>>> python test_copylist.py
a=[True, [True, True], True]
b=[True, [True, True], True]
a=[False, [[False], True], True]
b=[False, [[False], True], True]
# 可以看到当a的值改变时,b的值也会随之变化;
# 这是因为a和b均指向了相同的内存地址;
# 在C语言中这种情况是不会发生的

这显然不是我们想看到结果,如果要实现C中赋值的效果(深拷贝),就需要用到copy模块,模块中有copy.copy()copy.deepcopy()两个函数,我们看一下使用方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
# test_copylist2.py
import copy
a = [True, [True, True], False]
b = a
copy_a = copy.copy(a)
dcopy_a = copy.deepcopy(a)
print('Before change')
print('addr_of_a=',hex(id(a)))
print('addr_of_b=',hex(id(b)))
print('a=',a)
print('copy_a=',copy_a)
print('dcopy_a=',dcopy_a)

a[0] = False
a[1][0] = False
print('After change')
print('addr_of_a=',hex(id(a)))
print('addr_of_b=',hex(id(b)))
print('a=',a)
print('copy_a=',copy_a)
print('dcopy_a=',dcopy_a)
>>> python test_copylist2.py
Before change
addr_of_a= 0x7f8562f439c8
addr_of_b= 0x7f8562f439c8
a= [True, [True, True], False]
copy_a= [True, [True, True], False]
dcopy_a= [True, [True, True], False]
After change
addr_of_a= 0x7f8562f439c8
addr_of_b= 0x7f8562f439c8
a= [False, [False, True], False]
b= [False, [False, True], False] # a is b=True
copy_a= [True, [False, True], False]
# copy.copy无法拷贝第二级列表
dcopy_a= [True, [True, True], False]
# copy.deeocopy将创建一个新的变量并从源变量copy值

总结

如果想要实现C中的拷贝效果,即字面意义上的变量赋值,需要使用y=copy.deepcopy(x)(后续x的变化不会改变y的值)。