Python 当中对于拷贝, 分为两种类型. 一种是数字和字符串, 另一种就是列表, 元组, 字典等其他类型了.
一, 数字和字符串的拷贝
1, 赋值
举个栗子:
- a1 = 123123
- a2 = 123123
- # a2 = a1 # 赋值
- print(id(a1)) # 通过 id() 函数来打印变量在内存当中的地址
- print(id(a2))
输出结果是:
- 1959780298352
- 1959780298352
在以上代码块当中, a2 与 a1 所赋的值是一样的, 都是数字 123123. 因为 python 有一个重用机制, 对于同一个数字, python 并不会开辟一块新的内存空间, 而是维护同一块内存地址, 只是将该数字对应的内存地址的引用赋值给变量 a1 和 a2. 所以根据输出结果, a1 和 a2 其实对应的是同一块内存地址, 只是两个不同的引用罢了. 同样的, 对于 a2 = a1, 其实效果等同于 "a1 = 123123; a2 = 123123", 它也就是将 a1 指向 123123 的引用赋值给 a2. 字符串跟数字的原理雷同, 如果把 123123 改成 "abcabc" 也是一样的.
结论: 对于通过用 = 号赋值, 数字和字符串在内存当中用的都是同一块地址.
2, 浅拷贝
- import copy # 使用浅拷贝需要导入 copy 模块
- a1 = 123123
- a3 = copy.copy(a1) # 使用 copy 模块里的 copy() 函数就是浅拷贝了
- print(id(a1))
- print(id(a3))
输出结果是:
35233168 35233168
通过使用 copy 模块里的 copy() 函数来进行浅拷贝, 把 a1 拷贝一份赋值给 a3, 查看输出结果发现, a1 和 a3 的内存地址还是一样.
结论: 对于浅拷贝, 数字和字符串在内存当中用的也是同一块地址.
3, 深拷贝
举个栗子:
- import copy
- a1 = 123123
- a4 = copy.deepcopy(a1) # 深拷贝是用 copy 模块里的 deepcopy() 函数
- print(id(a1))
- print(id(a4))
输出结果为:
31432080 31432080
查看结果发现, 对于深拷贝, 数字和字符串在内存当中用的也是同一块地址.
所以综上所述, 对于数字和字符串的赋值, 浅拷贝, 深拷贝在内存当中用的都是同一块地址. 原理如下图:
二, 字典, 列表, 元组等其他类型的拷贝
1, 赋值
举个栗子:
- n1 = {"k1": "wu", "k2": 123, "k3": ["alex", 678]}
- n2 = n1 # 赋值
- print(id(n1))
- print(id(n2))
输出结果:
- 2235551677536
- 2235551677536
我们的栗子当中用了一个字典 n1, 字典里面嵌套了一个列表, 当我们把 n1 赋值给 n2 时, 内存地址并没有发生变化, 因为其实它也是只是把 n1 的引用拿过来赋值给 n2 而已.(我们用了一个字典来举例, 其他类型也是一样的)
原理如下图:
结论: 对于赋值, 字典, 列表, 元组等其他类型用的内存地址不会变化.
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"]))
输出结果:
第一层字典的内存地址:
6516024
6516096
第二层嵌套的列表的内存地址:
- 36995720
- 36995720
通过以上结果可以看出, 进行浅拷贝时, 我们的字典第一层 n1 和 n3 指向的内存地址已经改变了, 但是对于第二层里的列表并没有拷贝, 它的内存地址还是一样的. 原理如下图:
结论: 所以对于浅拷贝, 字典, 列表, 元组等类型, 它们只拷贝第一层地址.
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"]))
输出结果:
第一层字典的内存地址:
- 31157560
- 35463600
第二层嵌套的列表的内存地址:
- 35947144
- 35947336
通过以上结果发现, 进行深拷贝时, 字典里面的第一层和里面嵌套的地址都已经变了. 对于深拷贝, 它会拷贝多层, 将第二层的列表也拷贝一份, 如果还有第三层嵌套, 那么第三层的也会拷贝, 但是对于里面的最小元素, 比如数字和字符串, 这里就是 "wu",123,"alex",678 之类的, 按照 python 的机制, 它们会共同指向同一个位置, 它的内存地址是不会变的. 原理如下图:
结论: 对于深拷贝, 字典, 列表, 元组等类型, 它里面嵌套多少层, 就会拷贝多少层出来, 但是最底层的数字和字符串地址不变.
举个实际应用场景的栗子.
我们在维护服务器信息的时候, 经常会要更新服务器信息, 这时我们重新一个一个添加是比较麻烦的, 我们可以把原数据类型拷贝一份, 在它的基础上做修改.
栗子一, 使用浅拷贝
- 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 值也被修改了, 这并不是我们希望看到的.
栗子二, 使用深拷贝
- import copy
- dic = {
- "cpu": [80, ],
- "mem": [80, ],
- "disk": [80, ]
- }
- print('before', dic)
- new_dic = copy.deepcopy(dic)
- new_dic['cpu'][0] = 50
- print(dic)
- print(new_dic)
输出结果:
- before {'cpu': [80], 'mem': [80], 'disk': [80]}
- {'cpu': [80], 'mem': [80], 'disk': [80]}
- {'cpu': [50], 'mem': [80], 'disk': [80]}
使用深拷贝的时候, 发现只有新的字典的 cpu 值被修改了, 原来的字典里面的 cpu 值没有变. 大功告成!
注: 本文是根据老男孩课程内容整理而成的, 图片也是出自武 sir 的, 因为原文我找不到了, 所以下面贴一下武 sir 的博客地址. 本文仅供个人笔记使用, 如果有侵犯, 请联系我, 我立即撤销.
来源: https://www.cnblogs.com/doublexi/p/8745792.html