Python中 深拷贝和浅拷贝 不可变和可变数据类型 举例分析

Python中深拷贝和浅拷贝的可变和不可变数据类型情况举例
希望对正在学习Python的您有所帮助。如有错误之处望指出。谢谢。


在python的交互模式中实验

1.数据的赋值

In[18]: list1 = [1,2,3,4,5,6]
In[19]: list2 = list1
In[20]: id(list1)
Out[20]: 139639962104008
In[21]: id(list2)
Out[21]: 139639962104008

In[22]: list1.append("BMW")		# 列表中添加一个元素
In[23]: print(list1)
[1, 2, 3, 4, 5, 6, 'BMW']
In[24]: print(list2)
[1, 2, 3, 4, 5, 6, 'BMW']	# 原数据修改 被赋值的也跟着改变

In[25]: id(list1)
Out[25]: 139639962104008
In[26]: id(list2)
Out[26]: 139639962104008 	# 修改原数据内存地址没有改变
    						

    赋值是引用了列表的同一个内存地址,在列表中增加新值,列表中又多存储了一个新元素的地址,而列表本身的地址没有变化,所以list1和list2的id都没有改变并都添加了新的元素
    赋值就等于完全共享了资源,在python中使用引用计数的做法可以节省内存使用空间

2.列表的切片赋值和列表的修改情况

In[2]: list = [1,2,3,4,5,6]
In[3]: id(list)
Out[3]: 140213469483848

In[4]: list2 = list[:]		    # 切片赋值
In[5]: list2
Out[5]: [1, 2, 3, 4, 5, 6]
In[6]: id(list2)
Out[6]: 140213469654536			# 切片后赋值产生了新的内存地址

In[7]: list3 = list
In[8]: list3
Out[8]: [1, 2, 3, 4, 5, 6]
In[9]: id(list3)
Out[9]: 140213469483848			# 直接赋值 是共用同一个地址
In[10]: id(list2)
Out[10]: 140213469654536
In[11]: id(list)
Out[11]: 140213469483848

In[12]: list.append(1)
In[13]: list
Out[13]: [1, 2, 3, 4, 5, 6, 1]
In[14]: list2
Out[14]: [1, 2, 3, 4, 5, 6]		# 切片赋值的数据并没有发生变化
In[15]: list3
Out[15]: [1, 2, 3, 4, 5, 6, 1]

拷贝 在python中有两种拷贝方式,一种是深拷贝deepcopy,一种是浅拷贝copy

作用:浅拷贝可以节省内存空间,pytho中大多数都是浅拷贝。可以根据需求进行浅拷贝还是深拷贝。例如是备份重要数据,不想被修改就采用深拷贝。

1.浅拷贝copy

# 导入copy模块
import copy

list = [1, 2, [1, 2]]
list1 = copy.copy(list) # 浅拷贝
print(list)
print(list1)
print(id(list))
print(id(list1))

list[-1][0] = "a"  # 修改嵌套的里层列表
list[0] = "b"	 # 修改外层的列表
print(list)
print(list1)
print(id(list))
print(id(list1))

print("-------下面是切片赋值 修改数据------------")

list2 = [1, 2, [1, 2]]
list3 = list2[:]  # 切片赋值
print(list2)
print(list3)
print(id(list2))
print(id(list3))

list2[-1][0] = "a"  # 修改里层的列表
list2[0] = "b"		# 修改外层的列表
print(list2)
print(list3)
print(id(list2))
print(id(list3))

运行结果:

[1, 2, [1, 2]]
[1, 2, [1, 2]]
139845534794760
139845558797384
['b', 2, ['a', 2]]		# 原数据list修改
[1, 2, ['a', 2]]		# 浅拷贝后的list1 外层没有改变 内存改变了
139845534794760			# 内存地址还是修改前的地址
139845558797384		
-------下面是切片赋值 修改数据------------
[1, 2, [1, 2]]
[1, 2, [1, 2]]
140634594190344
140634594190856
['b', 2, ['a', 2]]
[1, 2, ['a', 2]]
140634594190344
140634594190856

修改外层的列表,外层不随之改变,修改里层的子列表时,确会改变。

2.深拷贝deepcopy

import copy

list = ['a', 2, ['b', 2]]
list1 = copy.deepcopy(list)		# 深拷贝
print(list)
print(list1)
print(id(list))
print(id(list1))

list[0] = 'y'  # 修改list的外层列表
list[-1][0] = 'z' # 修改list的内层列表
print(list)
print(list1)
print(id(list))
print(id(list1))

运行结果:
['a', 2, ['b', 2]]
['a', 2, ['b', 2]]
140159390786568
140159390787080
['y', 2, ['z', 2]]		# 原数据被修改
['a', 2, ['b', 2]]		# 深拷贝不会被修改
140159390786568
140159390787080

深拷贝 list内外层修改后 list1的内外侧没有随之改变

结论:

浅拷贝:只拷贝外层 不会拷贝数据中的子对象,可以理解为windows中的快捷方式

深拷贝:全部拷贝,完全复制

3.对于不可变类型的深浅拷贝

import copy
# 不可变类型元祖
a = (1, 2, 3)
b = copy.copy(a)		# 浅拷贝
c = copy.deepcopy(a)	# 深拷贝
print(a)
print(b)
print(c)
print(id(a))
print(id(b))
print(id(c))

# 向元祖中添加数据
a = a[:2] + ("a",) + a[2:]  # 其实是重新赋值了
# a[0] = "w" 元祖是不可变类型 不可修改
print(a)
print(b)
print(c)
print(id(a))
print(id(b))
print(id(c))

运行结果:
(1, 2, 3)
(1, 2, 3)
(1, 2, 3)
140484977573464
140484977573464
140484977573464
(1, 2, 'a', 3)		# 因为实际是重新赋值了 所以产生了新的内存地址
(1, 2, 3)			# 浅拷贝结果
(1, 2, 3)			# 深拷贝结果
140484977319272		# 因为实际是重新赋值了 所以产生了新的内存地址
140484977573464
140484977573464

在不可变类型中 不管深拷贝和浅拷贝 都是引用(指向)同一个内存地址

4.不可变类型嵌套可变类型的深浅拷贝

import copy

a = (1, 2, ["a", "b"])
b = copy.copy(a)		# 浅拷贝
c = copy.deepcopy(a)	# 深拷贝
print(a)
print(b)
print(c)
print(id(a))
print(id(b))
print(id(c))

a[-1][0] = 3   # 修改其中的可变类型嵌套列表的数据
# a[0] = "w" 这里内层是不可变类型 不可修改
print(a)
print(b)
print(c)
print(id(a))
print(id(b))
print(id(c))

运行结果:
(1, 2, ['a', 'b'])
(1, 2, ['a', 'b'])
(1, 2, ['a', 'b'])
140092667158104
140092667158104
140092666850616		# 深拷贝内存地址改变了
(1, 2, [3, 'b'])	# 原数据改变
(1, 2, [3, 'b'])	# 浅拷贝中的内层可变类型的数据内存地址改变了
(1, 2, ['a', 'b'])	# 深拷贝是新的内存地址 所以不变
140092667158104    
140092667158104		# 修改内层可变类型数据,浅拷贝外层内存地址不变
140092666850616


当遇到不可变类型时需要注意 嵌套的数据类型情况。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值