Python的赋值、浅拷贝与深拷贝

Python基本类型分类

可变类型:List、Dict、Set。在保持地址固定的情况下可改变取值
不可变类型:Number、String、Tuple。改变值的情况下必须新建一个对象。
结论:Python存储的数据类型中,指挥存储基本类型的数据,如不可变类型。如果需要重新存储已存在的对象,则使用引用的方式,指向原来的值。

Python中的集中复制方式

  • 赋值:对象引用
  • copy:浅拷贝,拷贝父对象,不拷贝对象内部的子对象
  • deepcopy:深拷贝,copy模块的deepcopy函数,完全拷贝父对象和子对象

赋值

当给一个变量赋值时,其实就是将对象的引用复制了一份给 a 和 b ,可以看到数字 5 原本的地址就是140716710627120,不管赋给哪个变量,地址都是同一个,也就是指向同一个对象。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
>>>
a=
>>>
b=a
>>>
id
(a)
>>>
id
(b)
>>>
id
(
)
>>>

浅拷贝

可以看到这里的 cd 和之前的 ad 地址不同,因为开启了新的内存空间。就算去修改 cd 的值,不会影响 ad 。但仅限于 cd 和 ad 两个对象,其各自的子对象还是指向同一个对象。

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
38
39
40
41
42
43
44
45
46
>>>
ad = {
"name"
:
"qiwsir"
,
"lang"
:
"python"
}
>>>
cd = ad.copy()
>>>
cd
{'name': 'qiwsir', 'lang': 'python'}
>>>
id
(ad)
>>>
id
(cd)
>>>
>>>
cd[
'name'
]=
'baidu'
>>>
cd
{'name': 'baidu', 'lang': 'python'}
>>>
ad
{'name': 'qiwsir', 'lang': 'python'}
>>>
>>>
id
(ad[
'name'
])
# 由于str是不可变对象,指向同一个字符串的地址是相同的
>>>
id
(cd[
'name'
])
>>>

然而,如果需要完全赋值一份真正的“副本”,浅拷贝是不能完成任务的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
>>>
x = {
"name"
:
"qiwsir"
,
"lang"
:[
"python"
,
"java"
,
"c"
]}
>>>
y = x.copy()
>>>
id
(x)
>>>
id
(y)
>>>

在 x 这个字典中,有 2 个键值对。一个是字符串name,一个是列表lang。通过copy()得到了两个不同地址的对象。
将 y 中列表其中一个值删除。你会发现连 x 中列表的值也发生了改变。

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
>>> y
[
'lang'
]
.remove
(
'c'
)
>>> y
{
'name'
:
'qiwsir'
,
'lang'
:
[
'python'
,
'java'
]
}
>>> x
{
'name'
:
'qiwsir'
,
'lang'
:
[
'python'
,
'java'
]
}
>>>

如果一个变量的值修改了,另一个变量也跟着改变,那么它们很可能是同一个地址,因此用id()看一下,果然是。

1
2
3
4
5
6
7
8
9
10
11
>>>
id
(x[
'lang'
])
>>>
id
(y[
'lang'
])
>>>

如果修改各自字典中的字符串,那么二者互不影响

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
>>> x[
'name'
]=
'baidu'
>>> x
{
'name'
:
'baidu'
,
'lang'
: [
'python'
,
'java'
]}
>>> y
{
'name'
:
'qiwsir'
,
'lang'
: [
'python'
,
'java'
]}
>>>

深拷贝

使用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
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
>>> 
import
copy
>>>
z = copy.deepcopy(x)
>>>
z
{
'name'
:
'baidu'
,
'lang'
: [
'python'
,
'java'
]}
>>>
x
{
'name'
:
'baidu'
,
'lang'
: [
'python'
,
'java'
]}
>>>
z[
'lang'
].append(
'javascript'
)
>>>
z
{
'name'
:
'baidu'
,
'lang'
: [
'python'
,
'java'
,
'javascript'
]}
>>>
x
{
'name'
:
'baidu'
,
'lang'
: [
'python'
,
'java'
]}
>>>

Python官方文档有对此说明:https://docs.python.org/3/library/copy.html

这里说到,浅拷贝和深拷贝的区别仅与符合对象有关:

  • 浅拷贝在复制复合对象时,在尽可能的范围内寻找被复制对象的对象的引用插入其中
  • 深拷贝在复制复合对象时,递归地将被复制对象的副本插入其中。

Python的赋值、浅拷贝与深拷贝
https://zhouyinglin.top/2022/09/09/Python的赋值、浅拷贝与深拷贝/
作者
小周
发布于
2022年9月9日
许可协议