Python基础【3】:Python中的深浅拷贝解析
深浅拷贝
在研究Python的深浅拷贝区别前需要先弄清楚以下的一些基础概念:
变量--引用--对象(可变对象,不可变对象)
切片(序列化对象)--拷贝(深拷贝,浅拷贝)
我是铺垫~
一、【变量--引用--对象】:
Python中一切皆对象,甚至连type本身都是type对象。
1 >>> type(type(n1)) 2 <class 'type'>
Python中的变量不同于与Java/c/c++不同,它是对象的引用,是动态型变量,数据类型无须提前声明,变量类型是根据对象的类型动态变化。
例如运行n=9,Python内部先在内存中找块地址来存储9,n只是对这个对象的引用,或者说将指针指向这个对象。
这里的整数对象9包含了两层含义:
1、数值为9
2、一个头部信息,告诉Python这是个int对象【可理解为一个指向int类型的指针】
小结:
变量是一个系统表的元素,拥有指向对象的连接的空间;
对象是被分配的一块内存,用来存储值;
引用是在变量赋值时自动形成的变量到对象的指针。
再来看下面的例子:
1 >>> a = 3 2 >>> b = a 3 >>> id(a);id(b) 4 1627390480 5 1627390480 6 >>> del a 7 >>> b 8 3
>>> a = 5
>>> b
3
可以看出,a和b共享引用,指向同一个内存地址(其id完全相同),所以将变量a删除或者将a赋值为4,都对变量b的数值无影响。
上面的例子有一个局限,就是数值类型对象是不可改变的,如果换成列表和元组对象呢?
二、【可变对象--不可变对象】
Python中的可变对象:可以修改的对象,列表和字典。
Python中的不可变对象:一旦创建就不可以修改的对象,字符串、元组、整数。
1 >>> L1 = [2,3,4] 2 >>> L2 = L1 3 >>> L2 4 [2, 3, 4] 5 >>> L1= [5,6] 6 >>> L2 7 [2, 3, 4] 8 >>> L1 = [1,2,3] 9 >>> L2 = L1 10 >>> L1[0] = 99 11 >>> L1;L2 12 [99, 2, 3] 13 [99, 2, 3] 14 #因为对象本身变了
如此一来,可变对象的变量共享引用就可能会有问题了(原本对源变量的修改,一不小心影响了其他的变量引用)。所以针对这种情景,才有了深浅copy。
那么如果不想改变上面L2的值有两种作法:切片和copy模块。
1 >>> L1 2 [99, 2, 3] 3 >>> L2 = L1[:] 4 >>> L1,L2 5 ([99, 2, 3], [99, 2, 3]) 6 >>> id(L1);id(L2) 7 3089554433480 8 3089554433992 9 >>> L1[0] = 100 10 >>> L1;L2 11 [100, 2, 3] 12 [99, 2, 3]
拷贝
切片技术的应用范围:序列对象,即列表、字符串、元组。
注:序列是一类基本数据类型(字符串/列表/元组)的统称,这些数据类型都含有一些共同的特性。
切片不能应用于字典,对于字典只有dic.copy()方法和dic.deepcopy()方法。
1 >>> dic = {'name':'bolen','age':18} 2 >>> [item for item in dir(dic) if not item.startswith('__')] 3 ['clear', 'copy', 'fromkeys', 'get', 'items', 'keys', 'pop', 'popitem', 'setdefault', 'update', 'values'] 4 5 #上面的语法和三元运算一样,x = 1 if n >2 else 3(或x = [2,1][n>2]),都是Python中的语法糖。
1 >>> import copy 2 >>> L3 = copy.copy(L1) 3 >>> L4 = copy.deepcopy(L1) 4 >>>
上面铺垫了这么多,就是想说深浅拷贝,既可应用于序列对象也可用于字典。
【浅拷贝】
浅拷贝只拷贝顶层对象或者说父级对象。
【深拷贝】
深拷贝是拷贝所有对象,包括顶级对象和镶套对象,或者说拷贝所有父对象和子对象。
闲言少叙,show code:
1 >>> n1 2 {'k1': 'lilei', 'k3': ['yy', 456], 'k2': 123} 3 >>> n4 = n1 4 >>> n2= copy.copy(n1) 5 >>> n3 = copy.deepcopy(n1) 6 >>> n1;n2;n3;n4 7 {'k1': 'bboo', 'k3': ['yy', 456], 'k2': 123} 8 {'k1': 'bboo', 'k3': ['yy', 456], 'k2': 123} 9 {'k1': 'bboo', 'k3': ['yy', 456], 'k2': 123} 10 {'k1': 'bboo', 'k3': ['yy', 456], 'k2': 123} 11 >>> id(n1);id(n2);id(n3);id(n4) 12 3089553992072 13 3089554432904 14 3089554359368 15 3089553992072 16 #深浅拷贝内存地址和源对象都不一样。 17 >>> n1['k1'] = 'bolen' 18 >>> n1;n2;n3;n4 19 {'k1': 'bolen', 'k3': ['yy', 456], 'k2': 123} 20 {'k1': 'bboo', 'k3': ['yy', 456], 'k2': 123} 21 {'k1': 'bboo', 'k3': ['yy', 456], 'k2': 123} 22 {'k1': 'bolen', 'k3': ['yy', 456], 'k2': 123} 23 >>> n1['k3'][1]=789 24 >>> n1;n2;n3;n4 25 {'k1': 'bolen', 'k3': ['yy', 789], 'k2': 123} 26 {'k1': 'bboo', 'k3': ['yy', 789], 'k2': 123} 27 {'k1': 'bboo', 'k3': ['yy', 456], 'k2': 123} 28 {'k1': 'bolen', 'k3': ['yy', 789], 'k2': 123} 29 #当有多层镶套时,源对象改变后,浅拷贝和共享引用的值都变了,而深拷贝没有变。
【总结】
深浅拷贝都是对源对象的复制,占用不同的内存空间
如果源对象只有一级目录的话,源做任何改动,不影响深浅拷贝对象
如果源对象不止一级目录的话,源做任何改动,都要影响浅拷贝,但不影响深拷贝
序列对象的切片其实是浅拷贝,即只拷贝顶级的对象