python中賦值與深淺拷貝

  和不少語言同樣,Python中也分爲簡單賦值、淺拷貝、深拷貝這幾種「拷貝」方式。javascript

  在學習過程當中,一開始對淺拷貝理解很模糊。不過通過一系列的實驗後,我發現對這三者的概念有了進一步的瞭解。java

 

  1、賦值app

  賦值算是這三種操做中最多見的了,咱們經過一些例子來分析下賦值操做:post

  str例學習

 

>>> a = 'hello'
>>> b = 'hello'
>>> c = a
>>> [id(x) for x in a,b,c]
[4404120000, 4404120000, 4404120000]

 

  由以上指令中,咱們能夠發現a, b, c三者的地址是同樣的。因此以上賦值的操做就至關於c = a = b = 'hello'。this

  賦值是系統先給一個變量或者對象(這裏是'hello')分配了內存,而後再將地址賦給a, b, c。因此它們的地址是相同的。spa

 

  list例code

>>> a = ['hello']
>>> b = ['hello']
>>> c = a
>>> [id(x) for x in a,b,c]
[4403975952, 4404095096, 4403975952]

  可是這種狀況卻不同了,a和b的地址不一樣。爲什麼?對象

  由於str是不可變的,因此一樣是'hello'只有一個地址,可是list是可變的,因此必須分配兩個地址。blog

 

  這時,咱們但願探究以上兩種狀況若是修改值會如何?

  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中也改變。這就符合不可變的對象修改會開闢新的空間,可變的對象修改不會開闢新空間。也進一步證實了淺拷貝僅僅是複製了容器中元素的地址


 

  、深拷貝

複製代碼
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. 深拷貝是在另外一塊地址中建立一個新的變量或容器,同時容器內的元素的地址也是新開闢的,僅僅是值相同而已,是徹底的副本。也就是說(新瓶裝新酒)。

相關文章
相關標籤/搜索