- 文章目录
- 前言
- 对象的三个属性
- 对象的深浅拷贝
近几年面试过程中,凡是说会Python的必会问到对象的深浅拷贝,这个问题也一直困扰了我许久,今天一起聊聊Python对象的深浅拷贝在内存中的实质
对象的三个属性首先我们必须知道程序在运行过程中是在计算机内存中进行的,而程序又由多个对象组成,所以我们要知道Python对象有三个属性:id、type、value
- id : 内存地址
- type : 对象所存储的数据类型
- value : 数据类型对应的值
>>> i = 5 >>> id(i) 140720048047920 >>> id(5) 140720048047920 >>> >>> type(i)>>> i 5 >>>
我们在赋值变量时,其实引用的就是内存中的一个数字地址编号,在使用对象时,由于这个数字地址不便于开发与记忆,所以统一用变量名来代替数字的地址,Python中万物皆对象,对象就有id、type、value。就像上图其实本质是将5的内存地址给了i。
对象的深浅拷贝直接赋值:就是对象的引用(别名) 浅拷贝(copy):拷贝父对象,不拷贝父对象内部的子对象 深拷贝(deepcopy):copy模块的deepcopy方法,完全拷贝父对象及其子对象
接下来我们用力一个栗子来说明深浅拷贝在内存中的运行过程
浅拷贝
import copy
a = [4, 5, 6, [2, 5]]
b = a.copy().copy()
print("a的内存ID", id(a))
print("b的内存ID", id(b))
print("a:", a)
print("b:", b)
b.append(10)
b[3][0] = 3
print("浅拷贝之后的结果")
print("a:", a)
print("b:", b)
运行结果:
a的内存ID 1619660494208 b的内存ID 1619660491776 a: [4, 5, 6, [2, 5]] b: [4, 5, 6, [2, 5]] 浅拷贝之后的结果 a: [4, 5, 6, [3, 5]] b: [4, 5, 6, [3, 5], 10]
a在内存中的地址引用如下
执行完 b = a.copy().copy()后内存地址变化如下
执行完 b.append(10)的内存地址如下
所以这个时候a是没变的,b发生了变化
接下来执行 b[3][0] = 3,内存地址引用变化如下
应为是引用的子对象发生了变化,所以a和b都发生改变
a: [4, 5, 6, [2, 5]] b: [4, 5, 6, [2, 5]] 浅拷贝之后的结果 a: [4, 5, 6, [3, 5]] b: [4, 5, 6, [3, 5], 10]
深拷贝
c = [4, 5, 6, [2, 5]]
d = copy.deepcopy(c)
print("c的内存ID", id(c))
print("d的内存ID", id(d))
print("c:", c)
print("d:", d)
d.append(10)
d[3][0] = 3
print("深拷贝之后的结果")
print("c:", c)
print("d:", d)
c = [4, 5, 6, [2, 5]]
执行后内存地址的引用如下
d = copy.deepcopy( c )
d.append(10)
d[3][0] = 3
运行结果:
c的内存ID 1619660492032 d的内存ID 1619660399872 c: [4, 5, 6, [2, 5]] d: [4, 5, 6, [2, 5]] 深拷贝之后的结果 c: [4, 5, 6, [2, 5]] d: [4, 5, 6, [3, 5], 10]
相信到这里,小伙伴们已经看懂了内存地址的不断引用的过程。总结下:使用copy都是浅拷贝,只是拷贝了父级对象的内存地址引用,没有逐一拷贝子级的内存地址引用,而deepcopy是深拷贝,直接递归拷贝了所有的引用。所以当改变子级别的可变对象的引用地址时,浅拷贝出来的对象和原来的都会发生改变,而深拷贝不会受影响,是两个完全独立的个体。



