亚洲免费在线-亚洲免费在线播放-亚洲免费在线观看-亚洲免费在线观看视频-亚洲免费在线看-亚洲免费在线视频

詳解Python核心編程中的淺拷貝與深拷貝

系統 1518 0

一、問題引出淺拷貝

首先看下面代碼的執行情況:

            
a = [1, 2, 3]
print('a = %s' % a) # a = [1, 2, 3]
b = a
print('b = %s' % b) # b = [1, 2, 3]
a.append(4) # 對a進行修改
print('a = %s' % a) # a = [1, 2, 3, 4]
print('b = %s' % b) # b = [1, 2, 3, 4]

b.append(5) # 對b進行修改
print('a = %s' % a) # a = [1, 2, 3, 4, 5]
print('b = %s' % b) # b = [1, 2, 3, 4, 5]
          

上面的代碼比較簡單,定義了一個變量a,它是一個數值[1, 2, 3]的列表,通過一個簡單的賦值語句 b = a 定義變量b,它同樣也是數值[1, 2, 3]的列表。

問題是:如果此時修改變量a,對b會有影響嗎?同樣如果修改變量b,對a又會有影響嗎?

從代碼運行結果可以看出,無論是修改b還是修改a(注意這種修改的方式,是用append,直接修改原列表,而不是重新賦值),都另一方都是有影響的。

當然這個原因其實很好理解,變量a指向的是列表[1, 2, 3]的地址值,當用 = 進行賦值運算時,b的值也相應的指向的列表[1, 2, 3]的地址值。在python中,可以通過id(變量)的方法來查看地址值,我們來查看下a,b變量的地址值,看是不是相等:

            
# 注意,不同機器上,這個值不同,但只要a,b兩個變量的地址值是一樣的就能說明問題了
print(id(a)) # 4439402312
print(id(b)) # 4439402312
          

所以原理如下圖所示:

詳解Python核心編程中的淺拷貝與深拷貝_第1張圖片

因此,只要是在地址值:4439402312上的列表進行修改的話,a,b都會發生變化。(注意我這里說的修改,是在地址值為:4439402312上的列表進行的修改,而不說對變量a進行修改,因為對變量a的修改方式有兩種,本文結尾會解釋為什么不說對變量a進行修改)?。所以我們便引出了以下概念:

對于這種是將引用進行拷貝賦值給另一個變量的方式(即拷貝的是地址值),我們稱之為淺拷貝。

二、如何進行深拷貝

python中實現深拷貝的方式很簡單,只需要引入copy模塊,調用里面的deepcopy()的方法即可,示例代碼如下:

            
import copy
a = [1, 2, 3]
b = copy.deepcopy(a)

print('a = %s' % a) # a = [1, 2, 3]
print('b = %s' % b) # b = [1, 2, 3]

b.append(4)
print('a = %s' % a) # a = [1, 2, 3]
print('b = %s' % b) # b = [1, 2, 3, 4]
          

從代碼執行情況來看,我們已經實現了深拷貝。這時我們再來看下兩個變量的地址值:

            
print(id(a)) # 4321416008
print(id(b)) # 4321416200
          

果然就不一樣了。我們再通過一個圖來看下深拷貝的原理:

詳解Python核心編程中的淺拷貝與深拷貝_第2張圖片

三、copy模塊方法簡介

從深拷貝的實現過程,我們知道copy模塊,也使用了里面的deepcopy()方法。下面我們來介紹下copy模塊中的copy()與deepcopy()方法。

首先介紹我們已經使用過的deepcopy()方法,官方文檔介紹如下:

詳解Python核心編程中的淺拷貝與深拷貝_第3張圖片

簡單解釋下文檔中對這個方法的說明:

1. 返回值是對這個對象的深拷貝

2. 如果拷貝發生錯誤,會報copy.err異常

3. 存在兩個問題,第一是如果出遞歸對象,會遞歸的進行拷貝,第二正因為會遞歸拷貝,會導致出現拷貝過多的情況

4. 關于兩種拷貝方式的區別都是相對是引用對象

前兩點很好理解,針對第三點,我們用代碼進行解釋:

            
import copy
a = [1, 2, 3]
b = [3, 4, 5]
c = [a, b] # 列表嵌套
d = copy.deepcopy(c)
print('c = %s' % c) # c = [[1, 2, 3], [3, 4, 5]]
print('d = %s' % d) # d = [[1, 2, 3], [3, 4, 5]]
c.append(4)
print('c = %s' % c) # c = [[1, 2, 3], [3, 4, 5], 4]
print('d = %s' % d) # d = [[1, 2, 3], [3, 4, 5]]
c[0].append(4) # 相當于a.append(4)
print('c = %s' % c) # c = [[1, 2, 3, 4], [3, 4, 5], 4]
print('d = %s' % d) # d = [[1, 2, 3], [3, 4, 5]]
# a.append(4)
# print('c = %s' % c) # a = [1, 2, 3]
# print('d = %s' % d) # b = [1, 2, 3]
print(id(c)) # 4314188040
print(id(d)) # 4314187976
print(id(c[0])) # 4314186568
print(id(d[0])) # 4314187912
print(id(a)) # 4314186568
print(id(b)) # 4314186760
          

根據代碼,我們可以看到,當有嵌套對象,也就是文檔中提到的遞歸對象,從結果我們可以看到,嵌套對象會進行遞歸的深拷貝。即如果c里有一個a,那么不僅c會深拷貝,a同樣也會被深拷貝。原理如下圖所求:

詳解Python核心編程中的淺拷貝與深拷貝_第4張圖片

接下來我們再來看copy()方法:

官方文檔解釋的很簡單,它返回的就是對象的淺拷貝。但其實它會對最外層進行深拷貝,而如果有多層,第二層以后進行的就是淺拷貝了。代碼示例如下:

            
import copy
a = [1, 2, 3]
b = [3, 4, 5]
c = [a, b] # 列表嵌套
d = copy.copy(c)
print('c = %s' % c) # c = [[1, 2, 3], [3, 4, 5]]
print('d = %s' % d) # d = [[1, 2, 3], [3, 4, 5]]
c.append(4)
print('c = %s' % c) # c = [[1, 2, 3], [3, 4, 5], 4]
print('d = %s' % d) # d = [[1, 2, 3], [3, 4, 5]] 沒有發生變化,說明外層是深拷貝
c[0].append(4) # 相當于a.append(4)
print('c = %s' % c) # c = [[1, 2, 3, 4], [3, 4, 5], 4]
print('d = %s' % d) # d = [[1, 2, 3, 4], [3, 4, 5]] 發生了變化,說明內層是淺拷貝
# a.append(4)
# print('c = %s' % c) # c = [[1, 2, 3, 4], [3, 4, 5], 4]
# print('d = %s' % d) # d = [[1, 2, 3, 4], [3, 4, 5]] 發生了變化,說明內層是淺拷貝
print(id(c)) # 4322576648
print(id(d)) # 4322576584 d和c地址不同,進一步說明外層是深拷貝
print(id(c[0])) # 4322575176
print(id(d[0])) # 4322575176 c[0]和d[0]地址相同,進一步說明內層是淺拷貝
print(id(a)) # 4322575176
print(id(b)) # 4322575368
          

【注意】對于copy()方法,有特殊情況,比如元組類型,代碼示例如下:

            
import copy
a = [1, 2, 3]
b = [3, 4, 5]
c = (a, b) # 列表改成元組
d = copy.copy(c)
print(id(c)) # 4303015752
print(id(d)) # 4303015752 d和c地址相同
print(id(c[0])) # 4322575176
print(id(d[0])) # 4322575176 c[0]和d[0]地址相同,進一步說明內層是淺拷貝
          

可以看到,這里哪怕是最外層,也是淺拷貝。

這里因為copy方法內部有判斷,如果最外層的拷貝類型是不可變類型,則進行淺拷貝,反之則進行深拷貝。

至此,我們應該對淺拷貝的概念進行進一步加深理解:

如果對象中的所有元素,有一個是引用拷貝,則定義為是淺拷貝。(該定義不是官方定義,只是個人理解)

四、關于“修改”的一點說明

前面提到了修改變量,我認為修改是有兩種方式,第一種在原對象上進行修改,第二種就是重新賦值。看如下代碼:

            
import copy
a = [1, 2, 3]
b = a
a = [3, 4, 5]
print(a) # [3, 4, 5]
print(b) # [1, 2, 3]
          

同樣是淺拷貝,但是發現修改a之后,b沒有發生變化。

在修改的時候,我們很容易想當然的通過重新賦值的方式來修改,但其實這種修改方式是有問題的。當給a再次賦值的時候,其實是將a重新指向了另外一塊地址區域,而原來的[1, 2, 3]那塊地址區域是沒有發生任何變化的,所以對于b來說,它指向的東西并沒有改變。

這也解釋了之前文檔中關于deepcopy方法的一個說明,為什么只對引用對象有用,因為簡單類型修改的方式就是重新賦值。簡單理解就是你沒辦法通過簡單類型的變量直接通過.來調用自身的方法,都只能重新賦值來改變,那么都會指向新的地址。

以上就是本片文章關于Python核心編程中的淺拷貝與深拷貝的全部內容,大家可以把學習的心得寫在下面的留言區,感謝你對腳本之家的支持。


更多文章、技術交流、商務合作、聯系博主

微信掃碼或搜索:z360901061

微信掃一掃加我為好友

QQ號聯系: 360901061

您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。

【本文對您有幫助就好】

您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描上面二維碼支持博主2元、5元、10元、自定義金額等您想捐的金額吧,站長會非常 感謝您的哦!!!

發表我的評論
最新評論 總共0條評論
主站蜘蛛池模板: 国产精品公开免费视频 | 成人国产精品久久久免费 | 国产精品久久久久影院免费 | 经典国产乱子伦精品视频 | 久久亚洲精品一区成人 | 日本香蕉网 | 亚洲视频一区二区在线观看 | 干成人网| 免费一级毛毛片 | 欧美日韩有码 | 国产乱人伦偷精品视频不卡 | 六月婷婷中文字幕 | 一区毛片| 国产一区二区影院 | 国产亚洲精品久久久久久小说 | 国产欧美精品国产国产专区 | 成年人视频在线免费 | 免费福利在线视频 | 亚洲精品影院一区二区 | 久久久精品午夜免费不卡 | 国产亚洲漂亮白嫩美女在线 | 久久综合久久久久 | av线上免费观看 | 狠狠色综合久久婷婷色天使 | 欧美成人午夜影院 | 中国女人18毛片 | 成人影院wwwwwwwwwww | 成年女人毛片免费观看中文w | 欧美色精品 | 欧美黑人乱大交ⅹxxxxx | 亚洲精品欧美日韩 | 午夜免费看 | 桃色成人精品网站 | 精品国产日韩久久亚洲 | 亚洲精品国产一区二区 | 一级毛片秋霞特色大片 | 性xxx69xxx视频在线观看 | 午夜一级毛片免费视频 | 成人精品一区二区三区校园激情 | 国产女人精品性视频 | 四虎最新影院 |