python:深拷貝,淺拷貝,賦值參照

2020-08-10 09:56:23

第一部分轉載自:https://www.cnblogs.com/xueli/p/4952063.html

1. python的複製,深拷貝和淺拷貝的區別

在python中,物件賦值實際上是物件的參照。當建立一個物件,然後把它賦給另一個變數的時候,python並沒有拷貝這個物件,而只是拷貝了這個物件的參照

一般有三種方法,

alist=[1,2,3,["a","b"]]

 

(1)直接賦值,預設淺拷貝傳遞物件的參照而已,原始列表改變,被賦值的b也會做相同的改變

>>> b=alist
>>> print b
[1, 2, 3, ['a', 'b']]
>>> alist.append(5)
>>> print alist;print b
[1, 2, 3, ['a', 'b'], 5]
[1, 2, 3, ['a', 'b'], 5]

(2)copy淺拷貝,沒有拷貝子物件,所以原始數據改變,子物件會改變

>>> import copy

>>> c=copy.copy(alist)
>>> print alist;print c
[1, 2, 3, ['a', 'b']]
[1, 2, 3, ['a', 'b']]
>>> alist.append(5)
>>> print alist;print c
[1, 2, 3, ['a', 'b'], 5]
[1, 2, 3, ['a', 'b']]

>>> alist[3]
['a', 'b']
>>> alist[3].append('cccc')
>>> print alist;print c
[1, 2, 3, ['a', 'b', 'cccc'], 5]
[1, 2, 3, ['a', 'b', 'cccc']] 裏面的子物件被改變了

 

 

(3)深拷貝,包含物件裏面的自物件的拷貝,所以原始物件的改變不會造成深拷貝裡任何子元素的改變

>>> import copy

>>> d=copy.deepcopy(alist)
>>> print alist;print d
[1, 2, 3, ['a', 'b']]
[1, 2, 3, ['a', 'b']]始終沒有改變
>>> alist.append(5)
>>> print alist;print d
[1, 2, 3, ['a', 'b'], 5]
[1, 2, 3, ['a', 'b']]始終沒有改變
>>> alist[3]
['a', 'b']
>>> alist[3].append("ccccc")
>>> print alist;print d
[1, 2, 3, ['a', 'b', 'ccccc'], 5]
[1, 2, 3, ['a', 'b']]  始終沒有改變

 

 

2. python的複製,深拷貝和淺拷貝的背景與意義?

  不像matlab一樣,比如b=a,就是單純用a給b賦值,之後a怎麼變,b不會變化。c語言也是一樣。

如果想a怎麼變,b就怎麼變,c語言裡就提出了參照的概念,相當於別名了。

example:

int a; int &ra=a; //定義參照ra,它是變數a的參照,即別名

okay!這篇文章有詳解淺拷貝與深拷貝的本質原因:重新開闢記憶體來儲存

https://www.jianshu.com/p/9ed9b5ce7bb0

https://baijiahao.baidu.com/s?id=1627356407968660842&wfr=spider&for=pc 

這篇文章提出pythond的這個設計可以防止數據篡改,或者靈活改變

 

   在Python中,對物件有一種很通俗的說法,萬物皆物件。說的就是構造的任何數據型別都是一個物件,無論是數位、字串、還是函數,甚至是模組、Python都對當做物件處理。

所有Python物件都擁有三個屬性:身份、型別、值


 
  1. name="Li J"

  2. print(type(name))

  3. print(id(name))

  4. print(name)

  5.  
  6. #輸出:

  7. #<type 'str'>

  8. #140334394101408

  9. #Li J

     可變與不可變物件

    在Python中,按更新物件的方式,可以將物件分爲2大類:可變物件與不可變物件。

   可變物件: 列表、字典、集合。所謂可變是指可變物件的值可變,身份是不變的。

     不可變物件:數位、字串、元組。不可變物件就是物件的身份和值都不可變。新建立的物件被關聯到原來的變數名,舊物件被丟棄,垃圾回收器會在適當的時機回收這些物件。


 
  1. var1="python" #字串型別是不可變的

  2. print(id(var1))

  3. var1="java"

  4. print(id(var1))

  5.  
  6. a=[3,4] #list是可變的,

  7. print(id(a))

  8. a.append(3)

  9. print(id(a))

  10.  
  11. #輸出結果:

  12. 140591145210096

  13. 140591145211632

  14. 140590909362688

  15. 140590909362688

 

參照

     在Python程式中,每個物件都會在記憶體中申請開闢一塊空間來儲存該物件,該物件在記憶體中所在位置的地址被稱爲參照。在開發程式時,所定義的變數名實際就物件的地址參照。

     參照實際就是記憶體中的一個數字地址編號,在使用物件時,只要知道這個物件的地址,就可以操作這個物件,但是因爲這個數位地址不方便在開發時使用和記憶,所以使用變數名的形式來代替物件的數位地址。在Python中,變數就是地址的一種表示形式,並不開闢開闢儲存空間。

      就像 IP 地址,在存取網站時,實際都是通過 IP 地址來確定主機,而 IP 地址不方便記憶,所以使用域名來代替 IP 地址,在使用域名存取網站時,域名被解析成 IP 地址來使用。

通過一個例子來說明變數和變數指向的參照就是一個東西:


 
  1. b=18

  2. print(id(b))

  3. print(id(18))

  4.  
  5. 輸出:

  6. 29413312

  7. 29413312

      

淺拷貝:


 
  1. print("淺拷貝:")

  2. import copy

  3. b=[1,2,3,4,5]

  4. print("id b:",id(b))

  5. h=copy.copy(b)

  6. print("id h",id(h))

  7. print(h)

  8. h.append(6)

  9. print(h)

  10. print("id h",id(h))

  11. print(b) #淺拷貝新的列表h改變了,原來的b沒變。

  12.  
  13. b[1]='n' #列表元素改變後,新的列表也沒變

  14. print(h)

  15.  
  16. 輸出:

  17. 淺拷貝:

  18. ('id b:', 140165805110552)

  19. ('id h', 140165805110480)

  20. [1, 2, 3, 4, 5]

  21. [1, 2, 3, 4, 5, 6]

  22. ('id h', 140165805110480)

  23. [1, 2, 3, 4, 5]

  24. [1, 2, 3, 4, 5, 6]


 
  1. a = [1, 2]

  2. l1 = [3, 4, a]

  3. l2 = copy.copy(l1)

  4. print(l1)

  5. print(l2)

  6. print(id(l1))

  7. print(id(l2))

  8. a[0] = 11

  9.  
  10. print(id(l1))

  11. print(id(l2))

  12. print(l1)

  13. print(l2)

  14. 輸出:

  15. [3, 4, [1, 2]]

  16. [3, 4, [1, 2]]

  17. 140624327425704

  18. 140624326197400

  19. 140624327425704

  20. 140624326197400

  21. [3, 4, [11, 2]]

  22. [3, 4, [11, 2]]

 

 

   可以看出淺拷貝,相當於只拷貝了一層,到a那裏,a變化了,其值也就變化了。

    Python中有多種方式實現淺拷貝,copy模組的copy函數、物件的copy函數、工廠方法、切片等;大多數情況下,編寫程式時都是使用淺拷貝,除非有特定的需求;淺拷貝的優點:拷貝速度快,佔用空間少,拷貝效率高

   深拷貝

區別於淺拷貝只拷貝頂層參照,深拷貝會逐層進行拷貝,直到拷貝的所有參照都是不可變參照爲止。


 
  1. a = [1, 2]

  2. l1 = [3, 4, a]

  3. l2 = copy.deepcopy(l1)

  4. print(l1)

  5. print(l2)

  6. print(id(l1))

  7. print(id(l2))

  8. a[0] = 11

  9.  
  10. print(id(l1))

  11. print(id(l2))

  12. print(l1)

  13. print(l2)

  14.  
  15. 輸出:

  16. [3, 4, [1, 2]]

  17. [3, 4, [1, 2]]

  18. 140673014398488

  19. 140672779715720

  20. 140673014398488

  21. 140672779715720

  22. [3, 4, [11, 2]]

  23. [3, 4, [1, 2]]

 

爲什麼Python預設的拷貝方式是淺拷貝?

時間角度:淺拷貝花費時間更少;

空間角度:淺拷貝花費記憶體更少;

效率角度:淺拷貝只拷貝頂層數據,一般情況下比深拷貝效率高。

 

 

參考文獻

https://www.jianshu.com/p/9ed9b5ce7bb0

https://baijiahao.baidu.com/s?id=1627356407968660842&wfr=spider&for=pc