Python浅拷贝和深拷贝的区别和理解

浅拷贝和深拷贝的区别和理解

python中的数据类型

  1. python3中有6个标准的数据类型

    • Number (数字):int、float、bool、complex.
    • String (字符串)
    • List (列表)
    • Tuple (元组)
    • Set (集合)
    • Dictionary (字典)
  2. 数据类型的特点

  • 可变数据类型: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]: 140734506918544

In [4]: id(b)
Out[4]: 140734506918544

In [5]: a = -6

In [6]: b = -6

In [7]: id(a)
Out[7]: 2722944215248

In [8]: id(b)
Out[8]: 2722944213584

In [9]: p = 257

In [10]: q = 257

In [11]: id§
Out[11]: 2722944214640

In [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. 总结

  1. 对于不可变类型Number、String、Tuple,浅拷贝仅仅只是地址指向,不会开辟新的空间。
  2. 对于可变类型List、Set、Dictionary,浅拷贝会开辟新的地址空间(仅仅是最顶层开辟了新的地址,里层元素的地址还是一样的)。
  3. 浅拷贝后,改变原始对象中不可变类型中元素的值时,不会影响拷贝对象;改变原始对象中可变类型中元素的值时,会同时影响拷贝对象。(操作拷贝对象同理)

深拷贝

使用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. 深拷贝,除了顶层拷贝,还对子元素也进行了拷贝
  2. 经过深拷贝后,原始对象和拷贝对象中所有的元素地址都不在有相同的

其他拷贝方式

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地址不一样是因为它们本身是可变类型,所以开辟了新的地址空间,但是里层元素地址不变,所以是浅拷贝。

  1. 字典的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也是浅拷贝。

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值