浅拷贝和深拷贝
拷贝函数是专门为可变数据类型list、set、dict应用的一种函数。作用是,当一个值指向另一个值的时候,也不会影响指向的值,如果被指向的数据是可变数据,那么它一旦被批改,指向的数据也会随之扭转。
什么是可变数据和不可变数据
咱们来举一个例子,整型是不可变的数据,那么为什么是不可变的数据呢?一个数据是不是可变的就要关系到python的缓存机制。
当一个数据发生变化,如果它的内存地址没有发生变化,就阐明这是一个可变数据。
比如说,咱们当初创立一个值是a的变量,它的值是100,而后让这个数值发生变化,观察者个变量的内存地址是否产生了变动。
a = 100print(a, id(a)) # 100 1610845392a += 100print(a, id(a)) # 200 1610848592
咱们发现数值产生了变动,变量的内存也跟着产生了变动,咱们再创立一个变量b,值也是整型100
b = 100print(b, id(b)) # 100 1610845392
发现b的内存地址和a的内存地址是一样的,也就是说,像整型这样的数据类型,一个数字就独占一个内存地址,当某个指向这个值的变量,产生了变动的时候,不是这个变量的值要扭转,而是这个变量要寻找扭转后的值的内存地址,而后从新的指向它。只有你的硬件不重新启动,那么这个内存地址就永远也不会发生变化了,这样的数据就是不可变数据。
那么,反之就是可变数据,指的就是当变量指向的值发生变化之后,在这个内存地址上的值实打实的发生变化的值,就是可变数据类型。
比方列表,列表产生扭转之后,是在原有的根底上发生变化的,所以内存地址是不会扭转的,这就是可变数据类型,可变数据类型没有内存缓存机制,不能节俭内存,所以截然不同的数据,他们的内存地址可能是不雷同的。
a = [1, 2]print(a, id(a)) # [1, 2] 1528536069896a.append(3)print(a, id(a)) # [1, 2, 3] 1528536069896# b 和 a的值雷同,然而内存地址不雷同b = [1, 2, 3]print(b, id(b)) # [1, 2, 3] 1528536069832
那么拷贝函数是干什么的?
在咱们的理论工作当中,常常会应用的一种操作就是定义一个变量,它的值间接就赋给了一个原有的变量之上。可是变量定义之后咱们绝不是用来作为一个陈设的,而是要做运算、或者是做一个长期的存储,那么原有的变量的值是要扭转的,问题就来了,如果是一个不可变的数据还好,如果是可变的数据,间接的赋值他们的内存地址是雷同的, 如果一个变量的值发生变化,同内存地址的的值就都产生扭转了,咱们的向要长期存储的值也就不再是咱们想要的那个值了,这是绝大多数的时候咱们不想看到的后果。
咱们拿整型为例,变量a间接赋值给变量b,这个时候的变量a b 的值是雷同的,然而如果变量a的值产生了变动,是丝毫不影响变量b的值的。
a = 100print(a, id(a)) # 100 1610845392b = aprint(b, id(b)) # 100 1610845392a += 100print(a, id(a)) # 200 1610848592print(b, id(b)) # 100 1610845392
然而如果是可变数据就不是这样的状况了
a = [1, 2]print(a, id(a)) # [1, 2] 2077688035080b = aprint(b, id(b)) # [1, 2] 2077688035080a.append(3)print(a, id(a)) # [1, 2, 3] 2077688035080print(b, id(b)) # [1, 2, 3] 2077688035080
不可变数据的这个个性既是一个长处也是一个毛病,毛病就是如果咱们想要保留a变量发生变化之前的的一个情况的时候,是保留不下来的,这个时候就呈现了拷贝函数,它能够将可变数据变成不可变数据那样的成果。
浅拷贝
应用拷贝函数,将a变量放入作为参数放入函数中,应用b变量承受函数的返回值,就胜利的拷贝了变量a,变量b的内存地址和变量a的不一样,这样当它们其中一方发生变化之后,不会影响到另一方的数据。
# 拷贝函数不能间接应用,须要应用import导入copy模块,copy模块的copy函数就是浅拷贝import copya = [1, 2, 3]# 变量b不在间接是变量a的间接赋值了,而是通过copy函数的返回值b = copy.copy(a)# 他们的数值一样,然而内存地址不同,所以他们之间的任意一方发生变化都不会影响到第二方。print(a, id(a)) # [1, 2, 3] 2343743813320print(b, id(b)) # [1, 2, 3] 2343743813192a.append(4)print(a, id(a)) # [1, 2, 3, 4] 2343743813320print(b, id(b)) # [1, 2, 3] 2343743813192
然而如果变量a是一个二级容器或者是一个更多级容器,浅拷贝无奈拷贝第二级容器或者更多级的容器,所以当第二级容器或者是更多级的容器发生变化的时候,还是会发生变化,因为浅拷贝只能拷贝一级容器,所以多级容器的内存地址还是雷同的。
import copya = [[66,88], 2, 3]b = copy.copy(a)print(a, id(a)) # [[66, 88], 2, 3] 2431683163720print(b, id(b)) # [[66, 88], 2, 3] 2431683162184# 扭转二级容器a[0].append(100)print(a, id(a)) # [[66, 88, 100], 2, 3] 2431683163720print(b, id(b)) # [[66, 88, 100], 2, 3] 2431683162184# 浅拷贝不能拷贝二级及以上的容器print(id(a[0])) # 1582481372872print(id(b[0])) # 1582481372872
深拷贝
浅拷贝只能拷贝一级容器
所以诞生了深拷贝,深拷贝能够拷贝所有级别的容器。
import copya = [[66,88], 2, 3]# 深拷贝应用deepcopy函数b = copy.deepcopy(a)print(a, id(a)) # [[66, 88], 2, 3] 2168411158088print(b, id(b)) # [[66, 88], 2, 3] 2168411156552a[0].append(100)print(a, id(a)) # [[66, 88, 100], 2, 3] 2168411158088print(b, id(b)) # [[66, 88], 2, 3] 2168411156552# 深拷贝所有级别的容器print(id(a[0])) # 2168411158216print(id(b[0])) # 2168411122760
总结
应用深浅拷贝须要导入copy模块;
浅拷贝应用copy函数,只能拷贝一级容器的所有元素;
深拷贝应用deepcopy函数,能够拷贝所有级别容器的所有元素;
规范库copy
中只有copy
和deepcopy
两个函数对外开放应用;
因为深拷贝要拷贝的元素跟多,所以速度会远不如浅拷贝,在编程的过程中要留神防止造成多余的零碎累赘;
python中的不可变数据是Number、string、tuple,可变数据是list、set、dict;而拷贝就是专门为可变数据提供的,所以深浅拷贝只实用于list、set、dict,当然,可变数据应用拷贝函数也不会出错,然而没有意义。