首先要了解什么是拷贝、浅拷贝、深拷贝?python
拷贝:服务器
从原始数据复制一份出来,当复制成功后,这两份数据都是相互独立的,即修改任意一份数据都不会影响另外一份数据。app
浅拷贝:函数
python中,浅拷贝就是只是拷贝最外层的类型,简单来说就是拷贝了引用,并无拷贝内容. copy.copy()spa
深拷贝:code
对于一个对象全部层次的拷贝(递归拷贝)copy.deepcopy()对象
要知道深浅拷贝的区别,首先要知道python中什么是 可变数据类型 和 不可变数据类型blog
不可变数据类型的定义:递归
python中的不可变数据类型,不容许变量的值发生变化,若是改变了变量的值,至关因而新建了一个对象,而对于相同的值的对象,在内存中则只有一个对象,内部会有一个引用计数来记录有多少个变量引用这个对象.内存
python中 不可变数据类型:
可变数据类型的定义:
可变数据类型,容许变量的值发生变化,即若是对变量进行append、+=等这种操做后,只是改变了变量的值,而不会新建一个对象,变量引用的对象的地址也不会变化,不过对于相同的值的不一样对象,在内存中则会存在不一样的对象,即每一个对象都有本身的地址, 至关于内存中对于同值的对象保存了多份,这里不存在引用计数,是实实在在的对象。
python中 可变数据类型:
经过python中数据类型的分类,咱们谈论如下几种的拷贝:
不可变数据类型:
可变数据类型:
1. 赋值
咱们已知python中不可变数据类型:整型、浮点数、字符串、布尔值、元组
In [1]: a1 = 123 # 整型 In [2]: a2 = a1 In [3]: id(a1), id(a2) Out[3]: (1562980880, 1562980880) In [4]: b1 = 1.123 # 浮点数 In [5]: b2 = b1 In [6]: id(b1), id(b2) Out[6]: (1503028953024, 1503028953024) In [7]: c1 = 'hello' # 字符串 In [8]: c2 = c1 In [9]: id(c1), id(c2) Out[9]: (1503040484272, 1503040484272) In [10]: d1 = True # 布尔值 In [11]: d2 = d1 In [12]: id(d1), id(d2) Out[12]: (1562722720, 1562722720) In [13]: e1 = (1, 2, 3, 'hkey') # 元组 In [14]: e2 = e1 In [15]: id(e1), id(e2) Out[15]: (1503040349032, 1503040349032)
经过以上的例子,a一、a2 赋值的值是同样的。由于python有一个重用机制,对于 不可变数据类型 来讲,python并不会开辟一块新的内存空间,而是维护同一块内存地址,只是将 不可变数据类型 对应的地址引用赋值给变量a一、a2。因此根据输出结果,a1和a2其实对应的是同一块内存地址,只是两个不一样的引用。
结论:对于 不可变数据类型 经过'='赋值,不可变数据类型在内存当中用的都是同一块地址。
2. 浅拷贝
In [1]: import copy In [2]: a1 = 3.14 In [3]: a2 = copy.copy(a1) In [4]: id(a1), id(a2) Out[4]: (1690132070168, 1690132070168)
经过使用copy模块里的copy()函数来进行浅拷贝,把a1拷贝一份赋值给a2,查看输出结果发现,a1和a2的内存地址仍是同样。
结论:对于 不可变数据类型 经过浅拷贝,不可变数据类型在内存当中用的都是同一块地址。
3. 深拷贝
In [1]: import copy In [2]: a1 = 'hello' In [3]: a2 = copy.deepcopy(a1) In [4]: id(a1), id(a2) Out[4]: (1645307287064, 1645307287064)
经过使用copy模块里的deepcopy()函数来进行深拷贝,把a1拷贝一份赋值给a2,查看输出结果发现,a1和a2的内存地址仍是同样。
结论:对于 不可变数据类型 经过深拷贝,不可变数据类型在内存当中用的都是同一块地址。
这里能够对 不可变数据类型 下一个定义了:
对于python中 不可变数据类型 的赋值、浅拷贝、深拷贝在内存中都是指向同一内存地址。以下图:
1. 赋值
In [1]: n1 = {"k1": "wu", "k2": 123, "k3": ["alex", 678]} In [2]: n2 = n1 In [3]: id(n1), id(n2) Out[3]: (1888346752712, 1888346752712)
在上面的例子中,咱们使用了列表嵌套列表的方式,赋值后内存空间地址是一致的。其中原理以下图:
结论:对于 可变数据类型 进行赋值内存地址是不会变化的。
2. 浅拷贝
import copy n1 = {"k1": "wu", "k2": 123, "k3": ["alex", 678]} n3 = copy.copy(n1) # 浅拷贝 print("第一层字典的内存地址:") print(id(n1)) print(id(n3)) print("第二层嵌套的列表的内存地址:") print(id(n1["k3"])) print(id(n3["k3"])) 执行结果: 第一层字典的内存地址: 2260623665800 2260625794440 第二层嵌套的列表的内存地址: 2260626131144 2260626131144
经过以上结果能够看出,进行浅拷贝时,咱们的字典第一层n1和n3指向的内存地址已经改变了,可是对于第二层里的列表并无拷贝,它的内存地址仍是同样的。原理以下图:
结论:对于 python 可变数据类型,浅拷贝只能拷贝第一层地址。
3. 深拷贝
import copy n1 = {"k1": "wu", "k2": 123, "k3": ["alex", 678]} n4 = copy.deepcopy(n1) # 深拷贝 print("第一层字典的内存地址:") print(id(n1)) print(id(n4)) print("第二层嵌套的列表的内存地址:") print(id(n1["k3"])) print(id(n4["k3"])) 执行结果: 第一层字典的内存地址: 2018569398920 2018574527176 第二层嵌套的列表的内存地址: 2018576058568 2018576056840
经过以上结果发现,进行深拷贝时,字典里面的第一层和里面嵌套的地址都已经变了。对于深拷贝,它会拷贝多层,将第二层的列表也拷贝一份,
若是还有第三层嵌套,那么第三层的也会拷贝,可是对于里面的最小元素,好比数字和字符串,这里就是“wu”,123,“alex”,678之类的,
按照python的机制,它们会共同指向同一个位置,它的内存地址是不会变的。原理以下图:
结论:对于 python 可变数据类型,它里面嵌套多少层,就会拷贝多少层出来,可是最底层的数字和字符串不变。
python 深浅拷贝的实例:
咱们在维护服务器信息的时候,常常会要更新服务器信息,这时咱们从新一个一个添加是比较麻烦的,咱们能够把原数据类型拷贝一份,在它的基础上作修改。
实例1:使用浅拷贝
import copy # 定义了一个字典,存储服务器信息 dic = { 'cpu':[80, ], 'mem':[80, ], 'disk':[80, ] } print('before', dic) new_dic = copy.copy(dic) new_dic['cpu'][0] = 50 # 更新 cpu 为 50 print(dic) print(new_dic) 执行结果: before {'cpu': [80], 'mem': [80], 'disk': [80]} {'cpu': [50], 'mem': [80], 'disk': [80]} {'cpu': [50], 'mem': [80], 'disk': [80]}
这时咱们会发现,使用浅拷贝时,咱们修改新的字典的值以后,原来的字典里面的cpu值也被修改了,这并非咱们但愿看到的。
实例2:使用深拷贝
import copy # 定义了一个字典,存储服务器信息 dic = { 'cpu':[80, ], 'mem':[80, ], 'disk':[80, ] } print('before', dic) new_dic = copy.deepcopy(dic) new_dic['cpu'][0] = 50 # 更新 cpu 为 50 print(dic) print(new_dic) 执行结果: before {'cpu': [80], 'disk': [80], 'mem': [80]} {'cpu': [80], 'disk': [80], 'mem': [80]} {'cpu': [50], 'disk': [80], 'mem': [80]}
使用深拷贝的时候,发现只有新的字典的cpu值被修改了,原来的字典里面的cpu值没有变。大功告成!