和不少语言同样,Python中也分为简单赋值、浅拷贝、深拷贝这几种“拷贝”方式。app
在学习过程当中,一开始对浅拷贝理解很模糊。不过通过一系列的实验后,我发现对这三者的概念有了进一步的了解。post
1、赋值学习
赋值算是这三种操做中最多见的了,咱们经过一些例子来分析下赋值操做:spa
str例code
>>> a = 'hello' >>> b = 'hello' >>> c = a >>> [id(x) for x in a,b,c] [4404120000, 4404120000, 4404120000]
由以上指令中,咱们能够发现a, b, c三者的地址是同样的。因此以上赋值的操做就至关于c = a = b = 'hello'。对象
赋值是系统先给一个变量或者对象(这里是'hello')分配了内存,而后再将地址赋给a, b, c。因此它们的地址是相同的。blog
list例内存
>>> a = ['hello'] >>> b = ['hello'] >>> c = a >>> [id(x) for x in a,b,c] [4403975952, 4404095096, 4403975952]
可是这种状况却不同了,a和b的地址不一样。为什么?字符串
由于str是不可变的,因此一样是'hello'只有一个地址,可是list是可变的,因此必须分配两个地址。it
这时,咱们但愿探究以上两种状况若是修改值会如何?
str例
>>> a = 'world' >>> [id(x) for x in a,b,c] [4404120432, 4404120000, 4404120000] >>> print a, b, c world hello hello
这时a的地址和值变了,可是b, c地址和值都未变。由于str的不可变性,a要从新赋值则需从新开辟内存空间,因此a的值改变,a指向的地址改变。b, c因为'hello'的不变性,不会发生改变。
list例
>>> a[0] = 'world' >>> [id(x) for x in a,b,c] [4403975952, 4404095096, 4403975952] >>> print a, b, c ['world'] ['hello'] ['world']
这时a, c的值和地址均改变,但两者仍相同,b不改变。因为list的可变性,因此修改list的值不须要另外开辟空间,只需修改原地址的值。因此a, c均改变。
在了解了以上的不一样点以后,咱们就能很好地分析浅拷贝和深拷贝了。
咱们均用list做为例子。
2、浅拷贝
1 >>> a = ['hello', [123, 234]] 2 >>> b = a[:] 3 >>> [id(x) for x in a,b] 4 [4496003656, 4496066752] 5 >>> [id(x) for x in a] 6 [4496091584, 4495947536] 7 >>> [id(x) for x in b] 8 [4496091584, 4495947536]
Line3,4能够看出a, b地址不一样,这符合list是可变的,应开辟不一样空间。那浅拷贝就是拷贝了一个副本吗?再看Line5 - 8,咱们发现a, b中元素的地址是相同的。若是说字符串'hello'地址一致还能理解,可是第二个元素是list地址仍一致。这就说明了浅拷贝的特色,只是将容器内的元素的地址复制了一份。
接着咱们尝试修改a, b中的值:
1 >>> a[0] = 'world' 2 >>> a[1].append(345) 3 >>> print 'a = ', a, '\n\r', 'b = ', b 4 a = ['world', [123, 234, 345]] 5 b = ['hello', [123, 234, 345]]
a中第一个元素str改变,可是b中未改变;a中第二个元素改变,b中也改变。这就符合不可变的对象修改会开辟新的空间,可变的对象修改不会开辟新空间。也进一步证实了浅拷贝仅仅是复制了容器中元素的地址。
2、深拷贝
1 >>> from copy import deepcopy 2 >>> a = ['hello', [123, 234]] 3 >>> b = deepcopy(a) 4 >>> [id(x) for x in a, b] 5 [4496066824, 4496066680] 6 >>> [id(x) for x in a] 7 [4496091584, 4496067040] 8 >>> [id(x) for x in b] 9 [4496091584, 4496371792]
深拷贝后,能够发现a, b地址以及a, b中元素地址均不一样。这才是彻底拷贝了一个副本。
修改a的值后:
1 >>> a[0] = 'world' 2 >>> a[1].append(345) 3 >>> print 'a = ', a, '\n\r', 'b = ', b 4 a = ['world', [123, 234, 345]] 5 b = ['hello', [123, 234]]
从Line4,5中能够发现仅仅a修改了,b没有任何修改。由于b是一个彻底的副本,元素地址均与a不一样,a修改,b不受影响。
总结:
1. 赋值是将一个对象的地址赋值给一个变量,让变量指向该地址(旧瓶装旧酒)。
2. 浅拷贝是在另外一块地址中建立一个新的变量或容器,可是容器内的元素的地址均是源对象的元素的地址的拷贝。也就是说新的容器中指向了旧的元素(新瓶装旧酒)。
3. 深拷贝是在另外一块地址中建立一个新的变量或容器,同时容器内的元素的地址也是新开辟的,仅仅是值相同而已,是彻底的副本。也就是说(新瓶装新酒)。