python中的深拷貝和淺拷貝問題

2022-09-08 21:39:22 字數 2726 閱讀 5832

平時寫python用慣了numpy的矩陣型別,只用python自帶的list做有關矩陣的(二維陣列的)處理的時候碰到各種bug。這裡是今日份的bug和解決方案。

在乙個程式中,我們希望用list實現乙個二維陣列,然後對其中的元素挨個根據下標的指引來進行賦值。我們對這個二維陣列也就是矩陣的初始化是這樣的:

m, n =5, 3

matrix = [[1] * n] * m

其中m,n分別是行數和列數。乍一看沒有什麼問題,但是在賦值的時候出現了這樣的一幕:

matrix

out[199]: [[1, 1, 1], [1, 1, 1], [1, 1, 1], [1, 1, 1], [1, 1, 1]]

matrix[1][0] = 233

matrix

out[201]: [[233, 1, 1], [233, 1, 1], [233, 1, 1], [233, 1, 1], [233, 1, 1]]

???

我們發現,雖然用的是二維陣列的下標,結果不僅僅我們的第1行第0列被賦值,而且所有其他行的第0列也跟著賦值了。這是什麼原因呢?

問題就處在我們的初始化的方法上。

在python 中,對乙個list後面用 乘號 再加上 數字 的方法來初始化乙個list,實際上是對這個list進行了淺拷貝(shallow copy),在python中,有深拷貝(deep copy)和 淺拷貝 的區別。簡單來講就是:深拷貝就是把要拷貝的物件整體複製乙份,存在新開闢的空間裡;而淺拷貝指的是,對於要拷貝的物件要複製乙份,但是對於其內部的子物件就不複製了,而是直接引用,也就是類似於新增了乙個鏈結而已,如下:

import copy

aout[210]: [888, 2, 3, [4, 5]]

b = copy.copy(a)

bout[212]: [888, 2, 3, [4, 5]]

b[0] = 233

bout[214]: [233, 2, 3, [4, 5]]

aout[215]: [888, 2, 3, [4, 5]]

b[3][1] = 666

bout[217]: [233, 2, 3, [4, 666]]

aout[218]: [888, 2, 3, [4, 666]]

這裡就很明顯了,我們對a做乙個淺拷貝,目的是b,然後我們對b進行操作,如果對list中的整數賦值,也就是物件中的元素賦值,那麼就只改變b中的這個位置的元素值,而a的不變;但是如果我們對list中的list,也就是子物件進行賦值,那麼我們法線,這個操作同樣也影響了a中的結果。

copy 這個模組裡的copy()是淺拷貝的函式,我們在試一下深拷貝deepcopy()函式:

a

out[219]: [888, 2, 3, [4, 666]]

b = copy.deepcopy(a)

bout[221]: [888, 2, 3, [4, 666]]

b[0] = 1

bout[223]: [1, 2, 3, [4, 666]]

aout[224]: [888, 2, 3, [4, 666]]

b[3][0] = 233

bout[226]: [1, 2, 3, [233, 666]]

aout[227]: [888, 2, 3, [4, 666]]

這就很科學了!兩個物件 a 和 b 互不打擾,很和諧。

另外,我們通常的賦值操作(list的直接賦值)又是怎樣的結果呢?

a

out[229]: [888, 2, 3, [4, 666]]

b = a

bout[231]: [888, 2, 3, [4, 666]]

b[0] = 1

bout[233]: [1, 2, 3, [4, 666]]

aout[234]: [1, 2, 3, [4, 666]]

b[3][0] = 1

bout[236]: [1, 2, 3, [1, 666]]

aout[237]: [1, 2, 3, [1, 666]]

可以看出來,直接賦值的結果是比淺拷貝還淺拷貝的。。。因為a和b根本上就是指向的同乙個物件!也就是說,a和b是這個物件的兩個引用(reference),修改乙個就會改變另乙個。

了解了這兩個概念的區別,就可以解釋上述的問題。首先,我們對

[1] * 3

# 建立了 1 的三個淺拷貝,得到了[1,1,1],此時我們修改某個1,不會影響其他的,因為這是int的元素,不是子物件

[[1] * 3] * 4 建立了list列表[1,1,1]的四個淺拷貝,這裡得到的list是以list為元素型別的,因此改變乙個就會影響其他

# 這就是我們開始會得到所有行都改變的原因,因為他們實際上指向的是同乙個東西!

改用如下方式初始化陣列,就可以得到乙個可以通過二維下標訪問的矩陣了。

matrix = [[ 1

for i in

range(n)] for i in

range(m)]

這就是之前整理過的list comprehension的方法生成list。

(貌似這篇文章裡有好多感嘆號。。。因為學藝不精。。。被這個bug折騰了半天才發現它。。。好氣。。。)

2023年03月30日20:07:40

python中的淺拷貝和深拷貝

不得不說 python核心程式設計 是一本好書,看到其中一節做一下隨筆。在python中,當建立乙個物件後,然後把它賦給另乙個物件時,python並沒有去拷貝這個物件,而是拷貝了這個物件的引用。看不懂沒關係,我們看乙個例子。raw list first second 12 32 copy list ...

python中的深拷貝和淺拷貝

淺拷貝 copy 不拷貝物件的內容,僅僅拷貝子物件的引用 深拷貝 deepcopy 會連同拷貝子物件的記憶體,對子物件的修改不會影響源物件 下面用 來測試 import copy deftest copy 淺拷貝測試 a 10 20,5,6 b copy.copy a print a a,end t...

python中的淺拷貝和深拷貝

本篇介紹下python中的深拷貝和淺拷貝,主要從基本型別 類 不可變型別等方面進行介紹。1.介紹拷貝之前首先應該明白is和 的區別,即is表示同乙個物件,比較的是值 a 1000 b 1000 a b true a is bfalse class person object def init sel...