浅拷贝和深拷贝的区别和理解
python中的数据类型
-
python3中有6个标准的数据类型
- Number (数字):int、float、bool、complex.
- String (字符串)
- List (列表)
- Tuple (元组)
- Set (集合)
- Dictionary (字典)
-
数据类型的特点
-
可变数据类型:List 、Dictionary、Set
当该数据类型的值发生了改变,那么它对应的内存地址不会发生改变。
-
不可变数据类型:Number、String 、Tuple
当该数据类型的值发生了改变,那么它对应的内存地址也发生改变。
浅拷贝
使用copy 模块中的copy方法实现浅拷贝
1. 不可变类型 Number、String、Tuple
# Number
In [1]: import copy
In [2]: num1 =1234
In [3]: num2 = copy.copy(num1)
In [4]: id(num1)
Out[4]: 1715986138896
In [5]: id(num2)
Out[5]: 1715986138896
# String
In [7]: str1 = 'python'
In [8]: str2 = copy.copy(str1)
In [9]: id(str1)
Out[9]: 1715956310400
In [10]: id(str2)
Out[10]: 1715956310400
# Tuple
In [11]: tup1 = (18, 'tom')
In [12]: tup2 = copy.copy(tup1)
In [13]: id(tup1)
Out[13]: 1715986376584
In [14]: id(tup2)
Out[14]: 1715986376584
对于不可变类型,浅拷贝仅仅是地址指向,不会开辟新的空间。
注意:虽然在赋值的时候也是地址指向,但是存在范围,当小于-5或者大于256的时候就会开辟新的空间,不在仅仅只地址指向。
In [1]: a = -5
In [2]: b = -5
In [3]: id(a)
Out[3]: 140734506918544In [4]: id(b)
Out[4]: 140734506918544In [5]: a = -6
In [6]: b = -6
In [7]: id(a)
Out[7]: 2722944215248In [8]: id(b)
Out[8]: 2722944213584In [9]: p = 257
In [10]: q = 257
In [11]: id§
Out[11]: 2722944214640In [12]: id(q)
Out[12]: 2722944214256
2. 可变类型List、Dictionary、Set
# Set
In [19]: set1 = {1, 2, 3, 4}
In [20]: set2 = copy.copy(set1)
In [21]: id(set1)
Out[21]: 1715986882856
In [22]: id(set2)
Out[22]: 1715985718632
In [23]: set1 = {1, 2, 3, 5}
In [24]: id(set1)
Out[24]: 1715986883304
In [25]: set2
Out[25]: {1, 2, 3, 4}
In [26]: id(set2)
Out[26]: 1715985718632
# Dictionary
In [63]: friends = ['GuanYu', 'ZhangFei']
In [64]: dict1 = {'name': 'Liubei', 'age': 22, 'friends': friends}
In [65]: dict2 = copy.copy(dict1)
In [66]: dict2
Out[66]: {'name': 'Liubei', 'age': 22, 'friends': ['GuanYu', 'ZhangFei']}
In [67]: id(dict1)
Out[67]: 1715986134792
In [68]: id(dict2)
Out[68]: 1715986136952
In [69]: dict1['name'] = 'LiuXuande'
In [70]: dict2
Out[70]: {'name': 'Liubei', 'age': 22, 'friends': ['GuanYu', 'ZhangFei']}
In [71]: friends[0] = 'GuanYunChang'
In [72]: dict2
Out[72]: {'name': 'Liubei', 'age': 22, 'friends': ['GuanYunChang', 'ZhangFei']}
# List
In [73]: friends = ['GuanYu', 'ZhangFei']
In [74]: friends1 = ['GuanYu', 'ZhangFei']
In [75]: new_friends1 = copy.copy(friends1)
In [76]: id(friends1)
Out[76]: 1715986731144
In [77]: id(new_friends1)
Out[77]: 1715986611720
3. 总结
- 对于不可变类型Number、String、Tuple,浅拷贝仅仅只是地址指向,不会开辟新的空间。
- 对于可变类型List、Set、Dictionary,浅拷贝会开辟新的地址空间(仅仅是最顶层开辟了新的地址,里层元素的地址还是一样的)。
- 浅拷贝后,改变原始对象中不可变类型中元素的值时,不会影响拷贝对象;改变原始对象中可变类型中元素的值时,会同时影响拷贝对象。(操作拷贝对象同理)
深拷贝
使用copy 模块中的deepcopy方法实现深拷贝
**1.不可变类型 Number、String、Tuple **
num1 = 17
num2 = copy.deepcopy(num1)
print("num1:" + str(id(num1))) # num1:140734643496272
print("num2:" + str(id(num1))) # num2:140734643496272
str1 = "hello"
str2 = copy.deepcopy(str1)
print("str1:" + str(id(str1))) # str1:2374732424616
print("str2:" + str(id(str2))) # str2:2374732424616
tup1 = (18, "Tom")
tup2 = copy.deepcopy(tup1)
print("tup1:" + str(id(tup1))) # tup1:2374732415240
print("tup2:" + str(id(tup2))) # tup2:2374732415240
2.可变类型List、Dictionary、Set
# List
list1 = ['ZhangFei', 'LuiBei']
list2 = copy.deepcopy(list1)
print("list1:" + str(id(list1))) # list1:3132302390024
print("list2:" + str(id(list2))) # list2:3132302379848
# Dict
dic1 = dic1 = {'name': 'Liubei', 'age': 22}
dic2 = copy.deepcopy(dic1)
print("dic1:" + str(id(dic1))) # dic1:3132272508072
print("dic2:" + str(id(dic2))) # dic2:3132302335936
# Set
set1 = {"A","B"}
set2 = copy.deepcopy(set1)
print("set1:" + str(id(set1))) # set1:3132302339432
print("set2:" + str(id(set2))) # set2:3132302339656
3.深拷贝会对子元素也进行拷贝(在之前的浅拷贝中,子元素是不会开辟新空间做拷贝的。)
list1 = [1, 2]
list2 = [3, 4]
num = 666
list3 = [list1, list2, num]
new_list3 = copy.deepcopy(list3)
list3[1] = [113, 114]
new_list3[2] = [223, 224]
print("list3: %s" % list3)
print('new_list3: %s' % new_list3)
list3: [[1, 2], [113, 114], 666]
new_list3: [[1, 2], [3, 4], [223, 224]]
print("id list3:" + str(id(list3)))
print("id list3[0]:" + str(id(list3[0])))
print("id list3[1]:" + str(id(list3[1])))
print("id list3[2]:" + str(id(list3[2])))
id list3: 2324638103112
id list3[0]: 2324637971656
id list3[1]: 2324638102920
id list3[2]: 2324636231152
print("id new_list3:" + str(id(new_list3)))
print("id new_list3[0]:" + str(id(new_list3[0])))
print("id new_list3[1]:" + str(id(new_list3[1])))
print("id new_list3[2]:" + str(id(new_list3[2])))
id new_list3: 2324638103254
id new_list3[0]: 2324638102856
id new_list3[1]: 2324638102728
id new_list3[2]: 2324638103048
4. 总结
- 深拷贝,除了顶层拷贝,还对子元素也进行了拷贝
- 经过深拷贝后,原始对象和拷贝对象中所有的元素地址都不在有相同的
其他拷贝方式
1.使用分片表达式进行拷贝(浅拷贝)
list1 = [1, 2]
list2 = [3, 4]
num = 666
list3 = [list1, list2, num]
new_list3 = list3[:]
print("list3: "+str(id(list3)))
print("list3[0]: "+str(id(list3[0])))
print("list3[1]: "+str(id(list3[1])))
print("list3[2]: "+str(id(list3[2])))
list3: 2429014537800
list3[0]: 2429014405960
list3[1]: 2429014395720
list3[2]: 2428984288752
print("new_list3: "+str(id(new_list3)))
print("new_list3[0]: "+str(id(new_list3[0])))
print("new_list3[1]: "+str(id(new_list3[1])))
print("new_list3[2]: "+str(id(new_list3[2])))
new_list3: 2429014537864
new_list3[0]: 2429014405960
new_list3[1]: 2429014395720
new_list3[2]: 2428984288752
llist3和new_list3地址不一样是因为它们本身是可变类型,所以开辟了新的地址空间,但是里层元素地址不变,所以是浅拷贝。
- 字典的copy方法可以拷贝一个字典(浅拷贝)**
In [3]: dict1 = {"name": "LiuBei", "num": [1, 2, 4]}
In [4]: dict2 = dict1.copy()
In [5]: id(dict1)
Out[5]: 3016047870312
In [6]: id(dict2)
Out[6]: 3016048008000
In [7]: id(dict1['name'])
Out[7]: 3016048067336
In [8]: id(dict2['name'])
Out[8]: 3016048067336
In [9]: id(dict2['num'])
Out[9]: 3016048783048
In [10]: id(dict1['num'])
Out[10]: 3016048783048
从结果来看,虽然两个字典的地址不一样,但是里层元素的地址仍然一样,所以仅仅是拷贝了最外层的的地址,里层地址不变,因此字典的copy也是浅拷贝。