Python中的賦值、淺拷貝與深拷貝

首先需要了解下幾個概念

 

  • 變量:是一個系統表的元素,擁有指向對象的連接空間
  • 對象:被分配的一塊內存,存儲其所代表的值
  • 引用:是自動形成的從變量到對象的指針
  • 類型:屬於對象,而非變量
  • 不可變對象:一旦創建就不可修改的對象,包括字符串、元組、數值類型

(該對象所指向的內存中的值不能被改變。當改變某個變量時候,由於其所指的值不能被改變,相當於把原來的值複製一份後再改變,這會開闢一個新的地址,變量再指向這個新的地址。)

  • 可變對象:可以修改的對象,包括列表、字典、集合

(該對象所指向的內存中的值可以被改變。變量(準確的說是引用)改變後,實際上是其所指的值直接發生改變,並沒有發生複製行爲,也沒有開闢新的地址,通俗點說就是原地改變。)

 

 

當我們寫:

a = 'python'

Python解釋器乾的事情:

1. 創建也變量a

2. 創建一個對象(分配一塊內存),來存儲值 'python'

3. 將變量與對象,通過指針連接起來,從變量到對象的連接稱之爲引用(變量引用對象)


 

1.賦值: 只是複製了新對象的引用,不會開闢新的內存空間。

並不會產生一個獨立的對象單獨存在,只是將原有的數據塊打上一個新標籤,所以當其中一個標籤被改變的時候,數據塊就會發生變化,另一個標籤也會隨之改變。

 

2.淺拷貝: 創建新對象,其內容是原對象的引用。

淺拷貝有三種形式: 切片操作,工廠函數,copy模塊中的copy函數。

如: lst = [1,2,[3,4]]

切片操作:lst1 = lst[:] 或者 lst1 = [each for each in lst]

工廠函數:lst1 = list(lst)

copy函數:lst1 = copy.copy(lst)

淺拷貝之所以稱爲淺拷貝,是它僅僅只拷貝了一層,拷貝了最外圍的對象本身,內部的元素都只是拷貝了一個引用而已,在lst中有一個嵌套的list[3,4],如果我們修改了它,情況就不一樣了。

淺複製要分兩種情況進行討論:

1)當淺複製的值是不可變對象(字符串、元組、數值類型)時和“賦值”的情況一樣,對象的id值(id()函數用於獲取對象的內存地址)與淺複製原來的值相同。

2)當淺複製的值是可變對象(列表、字典、集合)時會產生一個“不是那麼獨立的對象”存在。有兩種情況:

第一種情況:複製的對象中無複雜子對象,原來值的改變並不會影響淺複製的值,同時淺複製的值改變也並不會影響原來的值。原來值的id值與淺複製原來的值不同。

第二種情況:複製的對象中有複雜子對象(例如列表中的一個子元素是一個列表),如果不改變其中複雜子對象,淺複製的值改變並不會影響原來的值。 但是改變原來的值中的複雜子對象的值會影響淺複製的值。

 

3.深拷貝:和淺拷貝對應,深拷貝拷貝了對象的所有元素,包括多層嵌套的元素。深拷貝出來的對象是一個全新的對象,不再與原來的對象有任何關聯。

所以改變原有被複制對象不會對已經複製出來的新對象產生影響。

只有一種形式,copy模塊中的deepcopy函數

 

 

對於不可變對象的深淺拷貝

import copy
a=(1,2,3)

print("=====賦值=====")
b=a
print(a)
print(b)
print(id(a))
print(id(b))

print("=====淺拷貝=====")
b=copy.copy(a)
print(a)
print(b)
print(id(a))
print(id(b))

print("=====深拷貝=====")
b=copy.deepcopy(a)
print(a)
print(b)
print(id(a))
print(id(b))

結果:

=====賦值=====
(1, 2, 3)
(1, 2, 3)
43481128
43481128
=====淺拷貝=====
(1, 2, 3)
(1, 2, 3)
43481128
43481128
=====深拷貝=====
(1, 2, 3)
(1, 2, 3)
43481128
43481128

不可變對象類型,沒有被拷貝的說法,即便是用深拷貝,查看id的話也是一樣的,如果對其重新賦值,也只是新創建一個對象,替換掉舊的而已。

一句話就是,不可變類型,不管是深拷貝還是淺拷貝,地址值和拷貝後的值都是一樣的。

 

對於可變對象深淺拷貝

import copy
a=[1,2,3]

print("=====賦值=====")
b=a
print(a)
print(b)
print(id(a))
print(id(b))

print("=====淺拷貝=====")
b=copy.copy(a)
print(a)
print(b)
print(id(a))
print(id(b))

print("=====深拷貝=====")
b=copy.deepcopy(a)
print(a)
print(b)
print(id(a))
print(id(b))

結果:

=====賦值=====
[1, 2, 3]
[1, 2, 3]
37235144
37235144
=====淺拷貝=====
[1, 2, 3]
[1, 2, 3]
37235144
37191432
=====深拷貝=====
[1, 2, 3]
[1, 2, 3]
37235144
37210184

賦值: 值相等,地址相等

copy淺拷貝:值相等,地址不相等

deepcopy深拷貝:值相等,地址不相等

 

對於可變對象深淺拷貝(外層改變元素)

import copy
l=[1,2,3,[4, 5]]

l1=l #賦值
l2=copy.copy(l) #淺拷貝
l3=copy.deepcopy(l) #深拷貝
l.append(6)

print(l)  
print(l1)
print(l2)
print(l3)

結果:

[1, 2, 3, [4, 5], 6]     #l添加一個元素6
[1, 2, 3, [4, 5], 6]     #l1跟着添加一個元素6
[1, 2, 3, [4, 5]]        #l2保持不變
[1, 2, 3, [4, 5]]        #l3保持不變

 

 

對於可變對象深淺拷貝(內層改變元素)

import copy
l=[1,2,3,[4, 5]]

l1=l #賦值
l2=copy.copy(l) #淺拷貝
l3=copy.deepcopy(l) #深拷貝
l[3].append(6) 

print(l) 
print(l1)
print(l2)
print(l3)

結果:

[1, 2, 3, [4, 5, 6]]      #l[3]添加一個元素6
[1, 2, 3, [4, 5, 6]]      #l1跟着添加一個元素6
[1, 2, 3, [4, 5, 6]]      #l2跟着添加一個元素6
[1, 2, 3, [4, 5]]         #l3保持不變

1.外層添加元素時,淺拷貝不會隨原列表變化而變化;內層添加元素時,淺拷貝纔會變化。
2.無論原列表如何變化,深拷貝都保持不變。
3.賦值對象隨着原列表一起變化。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章