Python基础之赋值&深浅拷贝
一 概述
1.1 定义
赋值:在Python中对象的赋值其实就是对象的引用。当创建一个对象,把它赋值给另一个变量的时候,python并没有拷贝这个对象,只是拷贝了这个对象的引用而已
浅拷贝:拷贝了最外围的对象本身,内部的元素都只是拷贝了一个引用而已。也就是,把对象复制一遍,但是该对象中引用的其他对象不复制
深拷贝:外围和内部元素都进行了拷贝对象本身,而不是引用。也就是,把对象复制一遍,并且该对象中引用的其他对象也进行复制
1.2 术语解释
变量:是一个系统表的元素,拥有指向对象的连接空间
对象:被分配的一块内存,存储其所代表的值
引用:是自动形成的从变量到对象的指针
不可变对象:一旦创建就不可修改的对象,包括字符串、元组、数字
可变对象:可以修改的对象,包括列表、字典
注意:类型(int类型,long类型(python3已去除long类型,只剩下int类型的数据))属于对象,不是变量
二 赋值
请看下面一段代码:
original_list = ['js','html',['Python','C','C++'],{'name':'joe1991','age':18}]
new_lsit = original_list # 对象赋值
print('------原始列表数据展示------')
print(id(original_list))
print(original_list)
print([id(ele) for ele in original_list])
print('------赋值新列表数据展示------')
print(id(new_lsit))
print(new_lsit)
print([id(ele) for ele in new_lsit])
original_list[0] = 'JavaScript'
original_list[2].append('Java')
original_list[3]['gender'] = 'male'
print('------原始列表修改后数据展示------')
print(id(original_list))
print(original_list)
print([id(ele) for ele in original_list])
print('------赋值新列表修改后数据展示------')
print(id(new_lsit))
print(new_lsit)
print([id(ele) for ele in new_lsit])
执行结果:
------原始列表数据展示------
2518515317128
['js', 'html', ['Python', 'C', 'C++'], {'name': 'joe1991', 'age': 18}]
[2518515307944, 2518515308056, 2518515315720, 2518514495432]
------赋值新列表数据展示------
2518515317128
['js', 'html', ['Python', 'C', 'C++'], {'name': 'joe1991', 'age': 18}]
[2518515307944, 2518515308056, 2518515315720, 2518514495432]
------原始列表修改后数据展示------
2518515317128
['JavaScript', 'html', ['Python', 'C', 'C++', 'Java'], {'gender': 'male', 'name': 'joe1991', 'age': 18}]
[2518515342896, 2518515308056, 2518515315720, 2518514495432]
------赋值新列表修改后数据展示------
2518515317128
['JavaScript', 'html', ['Python', 'C', 'C++', 'Java'], {'gender': 'male', 'name': 'joe1991', 'age': 18}]
[2518515342896, 2518515308056, 2518515315720, 2518514495432]
分析代码:
第一步:使用一个original_list变量,指向一个list类型的对象
第二步:通过original_list对new_list变量进行赋值,那么new_list即指向original_list对应的对象(内存地址)
说明:“original_list即是new_list”,“original_list[i]即是new_list[i]”
第三步:对original_list进行修改
说明:对original_list的任何修改都会体现在new_list上。即在Python中对象的赋值其实就是对象的引用
注意点:str是不可变类型,所以当修改的时候会替换旧的对象,产生一个新的地址2518515342896
简图说明:
三 浅拷贝
还是一样,先来段代码:
import copy
original_list = ['js',111,['Python','C','C++'],{'name':'joe1991','age':18}]
new_lsit = copy.copy(original_list)
# 输出代码与上面一致
执行结果:
------原始列表数据展示------
2743648036872
['js', 111, ['Python', 'C', 'C++'], {'age': 18, 'name': 'joe1991'}]
[2743639332376, 1643524240, 2743648051400, 2743638912968]
------浅拷贝新列表数据展示------
2743639396936
['js', 111, ['Python', 'C', 'C++'], {'age': 18, 'name': 'joe1991'}]
[2743639332376, 1643524240, 2743648051400, 2743638912968]
------原始列表修改后数据展示------
2743648036872
['JavaScript', 111, ['Python', 'C', 'C++', 'Java'], {'age': 18, 'name': 'joe1991', 'gender': 'male'}]
[2743639367216, 1643524240, 2743648051400, 2743638912968]
------浅拷贝新列表修改后数据展示------
2743639396936
['js', 111, ['Python', 'C', 'C++', 'Java'], {'age': 18, 'name': 'joe1991', 'gender': 'male'}]
[2743639332376, 1643524240, 2743648051400, 2743638912968]
分析代码:
第一步:使用一个original_list变量,指向一个list类型的对象
第二步:通过copy模块里面的浅拷贝函数copy(),对original_list指向的对象进行浅拷贝,然后浅拷贝生成的新对象赋值给new_lsit变量
说明:浅拷贝会创建一个新的对象,这里“original_list不是new_list”,但是,对于对象中的元素,浅拷贝就只会使用原始元素的引用(内存地址),也就是说“original_list[i]即是new_list[i]”
第三步:对original_list进行修改
说明:由于list的第一个元素是不可变类型,所以original_list对应的list的第一个元素会使用一个新的对象2743639367216,但是list后面的字典或列表是可变类型,修改操作不会产生新的对象,所以original_list的修改结果会相应的反应到new_lsit上
简图说明:
说明一下,当我们使用下面的操作的时候,会产生浅拷贝的效果:
- 使用切片[:]操作
- 使用工厂函数(如list/dir/set)
- 使用copy模块中的copy()函数
四 深拷贝
import copy
original_list = ['js',111,['Python','C','C++'],{'name':'joe1991','age':18}]
new_lsit = copy.deepcopy(original_list)
# 输出代码与之前一致
执行结果
------原始列表数据展示------
1456661506056
['js', 111, ['Python', 'C', 'C++'], {'name': 'joe1991', 'age': 18}]
[1456660010688, 1644638352, 1456661507336, 1456659197896]
------深拷贝新列表数据展示------
1456661506568
['js', 111, ['Python', 'C', 'C++'], {'name': 'joe1991', 'age': 18}]
[1456660010688, 1644638352, 1456661507144, 1456660062536]
------原始列表修改后数据展示------
1456661506056
['JavaScript', 111, ['Python', 'C', 'C++', 'Java'], {'name': 'joe1991', 'age': 18, 'gender': 'male'}]
[1456660045296, 1644638352, 1456661507336, 1456659197896]
------深拷贝新列表修改后数据展示------
1456661506568
['js', 111, ['Python', 'C', 'C++'], {'name': 'joe1991', 'age': 18}]
[1456660010688, 1644638352, 1456661507144, 1456660062536]
分析代码:
第一步:使用一个original_list变量,指向一个list类型的对象
第二步:通过copy模块里面的深拷贝函数deepcopy(),对original_list指向的对象进行深拷贝,然后深拷贝生成的新对象赋值给new_lsit变量
说明:跟浅拷贝类似,深拷贝也会创建一个新的对象,这里“original_list不是new_list”,但是,对于对象中的元素,深拷贝都会重新生成一份(有特殊情况,下面会说明),而不是简单的使用原始元素的引用(内存地址),也就是说“original_list[i]不是new_list[i]”
第三步:对original_list进行修改
由于list的第一个元素是不可变类型,所以original_list对应的list的第一个元素会使用一个新的对象,但是list后面的字典或列表是可变类型,修改操作不会产生新的对象,但是由于“original_list[i]不是new_list[i]”,所以original_list的修改不会影响new_lsit
简图说明:
五 特殊情况
对于拷贝有一些特殊情况:
- 对于非容器类型(如数字、字符串、和其他’原子’类型的对象)没有拷贝这一说。也就是说,对于这些类型,”obj is copy.copy(obj)” 、”obj is copy.deepcopy(obj)”
- 如果元祖变量只包含原子类型对象,则不能深拷贝,如以下代码:
import copy
original_tuple = ('Python','C','C++')
new_tuple = copy.deepcopy(original_tuple)
print(original_tuple is new_tuple) # True
original_tuple = ('Python','C','C++',[])
new_tuple = copy.deepcopy(original_tuple)
print(original_tuple is new_tuple) # False
六 总结
1. 深浅拷贝都是对源对象的复制,占用不同的内存空间
2. 不可变类型对象:对于深浅拷贝毫无影响,最终的地址值和值都是相等的(特殊情况除外)
3. 可变类型对象:
- 赋值: 值相等,地址相等
- copy浅拷贝:值相等,地址不相等
- deepcopy深拷贝:值相等,地址不相等




浙公网安备 33010602011771号