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
>>> a=5
>>> b=a
>>> id(a)
140716710627120
>>> id(b)
140716710627120
>>> id(5)
140716710627120
>>>

浅拷贝

可以看到这里的 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
>>> ad = {"name":"qiwsir", "lang":"python"}
>>> cd = ad.copy()
>>> cd
{'name': 'qiwsir', 'lang': 'python'}
>>> id(ad)
2191309359104
>>> id(cd)
2191309359168
>>>
>>> cd['name']='baidu'
>>> cd
{'name': 'baidu', 'lang': 'python'}
>>> ad
{'name': 'qiwsir', 'lang': 'python'}
>>>
>>> id(ad['name']) # 由于str是不可变对象,指向同一个字符串的地址是相同的
2191309705072
>>> id(cd['name'])
2191309705072
>>>

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

1
2
3
4
5
6
7
>>> x = {"name":"qiwsir", "lang":["python", "java", "c"]}
>>> y = x.copy()
>>> id(x)
2191309704384
>>> id(y)
2191309416128
>>>

在 x 这个字典中,有 2 个键值对。一个是字符串name,一个是列表lang。通过copy()得到了两个不同地址的对象。

将 y 中列表其中一个值删除。你会发现连 x 中列表的值也发生了改变。

1
2
3
4
5
6
>>> y['lang'].remove('c') 
>>> y
{'name': 'qiwsir', 'lang': ['python', 'java']}
>>> x
{'name': 'qiwsir', 'lang': ['python', 'java']}
>>>

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

1
2
3
4
5
>>> id(x['lang']) 
2191309704128
>>> id(y['lang'])
2191309704128
>>>

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

1
2
3
4
5
6
>>> 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
>>> 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.cn/post/2c296012.html
作者
小周
发布于
2022年9月9日
更新于
2022年12月15日
许可协议