新手区 Python 里的 可变类型 vs 不可变类型,浅拷贝 vs 深拷贝

乖乖扫地 好好捉虫 · 2018年03月15日 · 2199 次阅读

在 python 中可变类型和不可变类型指的是内存地址与其对应的 value 值之间的关系是否可以改变
二者间的关系不可改变的即为不可变类型

不可变类型

字符串,元组,整型,浮点型都是不可变类型

如图中地址 XXX01 永远指向 1,XXX02 永远指向 2
☞创建变量
a=1
b=2
则将变量 a 的指针指向 XXX01,b 的指针指向 XXX02,使用内建函数 id() 输出可以看到此时 a 和 b 使用的内存地址不同

☞将 b 赋值给 c
b=2
c=b
则将变量 c 的指针也指向 XXX02(b 的内存地址),使用内建函数 id() 输出可以看到此时 b 和 c 使用了同一个内存地址

☞改变 b 的值
b-=1
b 的值变为 1,b 的指针指向 XXX01(a 的内存地址),使用内建函数 id() 输出可以看到此时 a 和 b 使用了同一个内存地址

TIPS
当 XXX01 被初次创建并赋值给变量 b 时,它的引用计数被设置为 1。当变量 b 赋值给变量 c 时,它的引用计数 +1.

可变类型

当内存地址对应的 value 值可以更改时即为 可变类型
列表,字典属于可变类型

如图中地址 XXX03 XXX04 指向 [1,2,3],也可指向 [1,2,3,4] 或其他不同的值
☞创建变量
m= [1,2,3]
n= [1,2,3]
虽然 m n 的值相同,但 m 的指针指向 XXX03,n 的指针指向 XXX04,使用内建函数 id() 输出可以看到此时 m 和 n 使用的内存地址不同

☞将 n 赋值给 r
n= [1,2,3]
r=n
变量 r 的指针指向 XXX04(n 的内存地址),使用内建函数 id() 输出可以看到此时 n 和 r 使用了同一个内存地址

☞改变 r 的值
r.append(4)
变量 r 的值变为 [1,2,3,4] 但是 n 的值也发生了改变,n 和 r 仍然指向的是 XXX04 的地址,也就是说 XXX04 对应的 value 值发生了变化

在实际应用中,我们经常会遇到从上游接口获取一个数据 a,将其赋值给 b,然后返回 c,但并不希望 b 在当前接口的变化影响 a,
如果获取的数据类型刚好是可变类型数据,如上述例子的列表,那么在对赋值后的列表处理时,原来的列表数据也对发生改变,显然是不符合我们的期望结果,python 做的还是很强大的,针对这种需求提供了 copy 方法,导入后即可调用
import copy
n= [1,2]
r=copy.copy(n) // r=n[:]
变量 r 的指针指向另一个与 n 不同的内存地址,但两者的 value 值相同,使用内建函数 id() 输出查看

☞改变 r 的值
r.append(3)
变量 r 的值变为 [1,2,3] , n 的值保持不变,n 和 r 指向不同的内存地址

copy 方法有两种:copy.copy() | copy.deepcopy(),顾名思义,一个是浅拷贝,一个是深拷贝

浅拷贝 copy.copy()

Code 标记
import copy
n=[1,2,['a','b']]
r=copy.copy(n)
r.append(3)
r[2].append('c')
print id(n),n
print id(r),r

浅拷贝没有拷贝子对象 ['a','b'],所以该列表数据改变时的影响是全局的,与前述赋值引用相同

深拷贝 copy.deepcopy()

Code 标记
import copy
n=[1,2,['a','b']]
r=copy.deepcopy(n)
r.append(3)
r[2].append('c')
print id(n),n
print id(r),r

深拷贝同时拷贝了父子对象,所以数据改变时只影响拷贝后的数据

暂无回复。
需要 登录 后方可回复, 如果你还没有账号请点击这里 注册