浅谈Python浅拷贝与深拷贝

目录

1. 可变类型与不可变类型

2. 赋值

3. 浅拷贝实现方式

4. 深拷贝


1. 可变类型与不可变类型

"""

可变类型: 列表、字典(字典中键值对的key值不能是可变类型)、集合
不可变类型: 数字类型(int, bool, float)、字符串、元组
可变类型:在地址不变的情况下,可以修改内容, 换言之,当该数据类型的对应变量的值发生了改变,那么它对应的内存地址不发生改变
不可变类型: 在地址不变的情况下,不可修改内容,换句话言之,当该数据类型的对应变量的值发生了改变,那么它对应的内存地址也会发生改变

总结:不可变数据类型更改后地址发生改变,可变数据类型更改地址不发生改变

@ 如果在函数内部针对参数进行赋值语句,修改局部变量的引用(保存数据),不会影响外部变量的引用
@ 如果调用函数传递的参数是可变类型,则在函数内部,使用方法(函数)修改了原来参数的内容,同样会改变外部的传递的参数

注意:不可变类型元组:元组内所存储的元素的地址不能改变,但是如果元组内所存储元素包含可变类型,如果可变类型中的值发生了变化,则会导致可变类型中元素的地址发生变化,然而元组中属于该可变类型的地址并没有发生变化

"""

2. 赋值

src_assignment = [[1, 2], [3, 4], 5]
dest_assignment = src_assignment # 赋值
print(src_assignment, id(src_assignment)) # [[1, 2], [3, 4], 5] 1910165229760
print(dest_assignment, id(dest_assignment)) # [[1, 2], [3, 4], 5] 1910165229760
print(id(src_assignment[0]), id(dest_assignment[0])) # 1910165230400 1910165230400
print(id(src_assignment[1]), id(dest_assignment[1])) # 1910165230336 1910165230336
print(id(src_assignment[0][0]), id(dest_assignment[0][0])) # 1910156912944 1910156912944
print('_^_^'*20)
# 修改列表src_assignment中元素的值
src_assignment.append(8)
src_assignment[1] = 4
src_assignment[0][0] = 8
# 打印修改后的列表元素值
print(src_assignment, id(src_assignment)) # [[8, 2], 4, 5, 8] 1910165229760
print(dest_assignment, id(dest_assignment)) # [[8, 2], 4, 5, 8] 1910165229760
print(id(src_assignment[0]), id(dest_assignment[0])) # 1910165230400 1910165230400
print(id(src_assignment[1]), id(dest_assignment[1])) # 1910156913040 1910156913040
print(id(src_assignment[0][0]), id(dest_assignment[0][0])) # 1910156913168 1910156913168
"""

赋值“=”:如果用 = 直接赋值, 是非拷贝方法, 其实就是将“=”两边的两个不同的变量同时指向了同一个内存空间, 且内存空间的值是相同的. 这两个列表是等价的, 修改其中任何一个列表都会影响到另一个列表, 即就是当一个变量对内存空间中的数值进行了修改之后, 在使用另一个变量去引用该内存空间中的变量时, 数值也会改变, 因为他们都指向同一个数据值.

"""

3. 浅拷贝实现方式

"""

1.不可变类型进行浅拷贝不会给拷贝的对象开辟新的内存空间,而只是拷贝了这个对象的引用(内容+地址).

2.可变类型进行浅拷贝只对可变类型的第一层对象进行拷贝, 只拷贝了这个对象的内容,而对拷贝的对象会开辟新的内存空间进行存储,子对象不进行拷贝.

"""
方式一:通过调用copy库实现深浅拷贝
first_list = [[1, 2], [3, 4], 5]
second_list = copy.copy(first_list)
print(first_list, id(first_list)) # [[1, 2], [3, 4], 5] 1744125645824
print(second_list, id(second_list)) # [[1, 2], [3, 4], 5] 1744125645760
print(id(first_list[0]), id(second_list[0])) # 1744125645952 1744125645952
print(id(first_list[1]), id(second_list[1])) # 1744125645888 1744125645888
print(id(first_list[0][0]), id(second_list[0][0])) # 1744120932656 1744120932656
print('^_^'*20)
# 修改列表list_first中元素的值
first_list.append(8)
first_list[1] = 4
first_list[0][0] = 8
# 打印修改后的列表元素值
print(first_list, id(first_list)) # [[8, 2], 4, 5, 8] 1744125645824
print(second_list, id(second_list)) # [[8, 2], [3, 4], 5] 1744125645760
print(id(first_list[0]), id(second_list[0])) # 1744125645952 1744125645952
print(id(first_list[1]), id(second_list[1])) # 1744120932752 1744125645888
print(id(first_list[0][0]), id(second_list[0][0])) # 1744120932880 1744120932880
"""

* 纯可变类型(不包含嵌套其他可变类型)
1.通过copy库实现浅拷贝, 实际上是创建一个新的对象, 其内容是原对象中元素的引用(地址不变,内容不变), 而地址是重新分配,但是对于其中的元素是没有进行拷贝的 --> 拷贝组合对象, 不拷贝子对象;
2.如果对其中元素修改,删除,增加不会影响到另一个变量中的元素的值, 因为原对象与新对象所指向的内存地址不同.

* 可变类型(包含嵌套其他可变类型)
1.通过copy库实现浅拷贝, 第一层, 是实现了深拷贝, 但对于其内嵌套的可变类型, 仍然是浅拷贝. 因为嵌套的可变类型保存的是地址, 复制过去的时候是把地址复制过去了, 嵌套的可变类型在内存中指向的还是同一个内存地址。
2.如果对其中所嵌套的元素内容进行修改,删除,增加则会影响到另一个变量中的元素的值, 因为他们都指向同一块内存地址, 修改相当于修改同一块内存地址中的内容.

"""
方式二:通过切片实现浅拷贝
three_list = [[1, 2], [3, 4], 5] 
four_list = three_list[:] # 切片
print(three_list, id(three_list)) # [[1, 2], [3, 4], 5] 1930643854016
print(four_list, id(four_list)) # [[1, 2], [3, 4], 5] 1930643853440
print(id(three_list[0]), id(four_list[0])) # 1930643854144 1930643854144
print(id(three_list[1]), id(four_list[1])) # 1930643854080 1930643854080
print(id(three_list[0][0]), id(four_list[0][0])) # 1930635602224 1930635602224
print('^_^'*20)
# 修改列表three_list中元素的值
three_list.append(8)
three_list[1] = 4
three_list[0][0] = 8
# 打印修改后的列表元素值
print(three_list, id(three_list)) # [[8, 2], 4, 5, 8] 1930643854016
print(four_list, id(four_list)) # [[8, 2], [3, 4], 5] 1930643853440
print(id(three_list[0]), id(four_list[0])) # 1930643854144 1930643854144
print(id(three_list[1]), id(four_list[1])) # 1930635602320 1930643854080
print(id(three_list[0][0]), id(four_list[0][0])) # 1930635602448 1930635602448
"""

切片操作:当采用切片操作对一个列表进行赋值给另外一个变量时, 这时不再是两个不同的变量同时指向一个内存地址空间中数据值, 因为切片操作会返回一个新的列表, 新的列表中的数据值就是切片之后的数据值, 且在内存中的地址空间不一样。其结果等同于通过copy库调用copy(方法)实现浅拷贝.

"""
方式三:通过list语法实现浅拷贝
five_list = [[1, 2], [3, 4], 5]
six_list = list(five_list) # list语法
print(five_list, id(five_list)) # [[1, 2], [3, 4], 5] 3077450519744
print(six_list, id(six_list)) # [[1, 2], [3, 4], 5] 3077450536000
print(id(five_list[0]), id(six_list[0])) # 3077450519872 3077450519872
print(id(five_list[1]), id(six_list[1])) # 3077450519808 3077450519808
print(id(five_list[0][0]), id(six_list[0][0])) # 3077445806384 3077445806384
print('^_^'*20)
# 修改列表five_list中元素的值
five_list.append(8)
five_list[1] = 4
five_list[0][0] = 8
# 打印修改后的列表元素值
print(five_list, id(five_list)) # [[8, 2], 4, 5, 8] 3077450519744
print(six_list, id(six_list)) # [[8, 2], [3, 4], 5] 3077450536000
print(id(five_list[0]), id(six_list[0])) # 3077450519872 3077450519872
print(id(five_list[1]), id(six_list[1])) # 3077445806480 3077450519808
print(id(five_list[0][0]), id(six_list[0][0])) # 3077445806608 3077445806608
"""

List语法构造操作:当采用list语法操作对一个列表进行赋值给另外一个变量时, 这时也不再是两个不同的变量同时指向一个内存地址空间中数据值, 因为list语法操作也会返回一个新的列表, 新的列表中的数据值就是list语法构造之后的数据值, 且在内存中的地址空间不一样。其结果也等同于通过copy库调用copy(方法)实现浅拷贝.与之类似的还有列表推导式,for循环遍历等.

"""

4. 深拷贝

"""

1.copy库中deepcopy()方法是深拷贝, 只要发现对象有可变类型就会对该对象到最后一个可变类型的每一层对象进行拷贝, 对每一层拷贝的对象都会开辟新的内存空间进行存储。

2.不可变类型进行深拷贝如果子对象没有可变类型,则只是拷贝了这个对象的引用(地址+内容),否则会对该对象到最后一个可变类型的每一层对象就行拷贝, 对每一层拷贝的对象都会开辟新的内存空间进行存储

3.可变类型进行深拷贝会对该对象到最后一个可变类型的每一层对象进行拷贝, 对每一层拷贝的对象都会开辟新的内存空间进行存储。

"""
seven_list = [[1, 2], [3, 4], 5]
eight_list = copy.deepcopy(seven_list)
print(seven_list, id(seven_list)) # [[1, 2], [3, 4], 5] 1806896747648
print(eight_list, id(eight_list)) # [[1, 2], [3, 4], 5] 1806896747072
print(id(seven_list[0]), id(eight_list[0])) # 1806896747776 1806896726080
print(id(seven_list[1]), id(eight_list[1])) # 1806896747712 1806896717760
print(id(seven_list[0][0]), id(eight_list[0][0])) # 1806888495408 1806888495408
print('^_^' * 20)
# 修改列表seven_list中元素的值
seven_list.append(8)
seven_list[1] = 4
seven_list[0][0] = 8
# 打印修改后的列表元素值
print(seven_list, id(seven_list)) # [[8, 2], 4, 5, 8] 1806896747648
print(eight_list, id(eight_list)) # [[1, 2], [3, 4], 5] 1806896747072
print(id(seven_list[0]), id(eight_list[0])) # 1806896747776 1806896726080
print(id(seven_list[1]), id(eight_list[1])) # 1806888495504 1806896717760
print(id(seven_list[0][0]), id(eight_list[0][0])) # 1806888495632 1806888495408
"""

1."深拷贝", 是指创建一个新的对象, 然后递归的拷贝原对象所包含的子对象. 深拷贝出来的对象与原对象没有任何关联, 对两个变量操作不会相互影响.
2.python中, 深拷贝在浅拷贝的基础上更进一步, 深拷贝保存的是数据的本身, 一旦进行深拷贝,除了拷贝后的内容和原本的内容完全一样, 而其他和原来的没有任何联系, 充分保证了数据的独立性. 深拷贝相当于在内存中开辟了一片新的内存, 不再用来保存数据的地址, 而是用新的地址来保存相同的数据.

"""
nine_copy = (1, 2, ["李四", "王五"])
ten_copy = copy.deepcopy(nine_copy)
print('nine_copy', id(nine_copy), nine_copy) # nine_copy 1866054492736 (1, 2, ['李四', '王五'])
print('ten_copy', id(ten_copy), ten_copy) # ten_copy 1866056887360 (1, 2, ['李四', '王五'])
print("^_^" * 10)
# 元组里面的可变类型子对象也会进行拷贝
print('nine_copy[1]', id(nine_copy[1]), nine_copy[1]) # nine_copy[1] 1866048629072 2
print('ten_copy[1]', id(ten_copy[1]), ten_copy[1]) # ten_copy[1] 1866048629072 2
print('nine_copy[2]', id(nine_copy[2]), nine_copy[2]) # nine_copy[2] 1866057012800 ['李四', '王五']
print('ten_copy[2]', id(ten_copy[2]), ten_copy[2]) # ten_copy[2] 1866057086400 ['李四', '王五']
"""

注意: 元组里面要是有可变类型对象,发现对象有可变类型就会该对象到最后一个可变类型的每一层对象进行拷贝.

"""
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值