從python列表賦值談shallow copy

2021-08-29 20:32:51 字數 3490 閱讀 7778

在高階語言中,變數是對記憶體及其位址的抽象。對於python而言,python的一切變數都是物件,變數的儲存,採用了引用語義的方式,儲存的只是乙個變數的值所在的記憶體位址,而不是變數本身。

上面的話可能有點難以理解,看例子:

a = 『abc』

定義變數 a 時,python直譯器幹了兩件事情:

1)在記憶體中建立了乙個』abc』的字串;

2)在記憶體中建立了乙個名為 a 的變數,並把它指向』abc』。

靜態語言如c++在定義變數時必須指定變數型別,如果賦值的時候型別不匹配,就會報錯。python作為動態語言,與靜態語言相比更靈活,正是這個原因。

舉個栗子:

a =

'abc'

b = a

a ='xyz'

print

(b)

讀者可以先思考一下結果是什麼?

如果你認為列印『abc』,那麼你可能理解了python變數;如果你認為列印『xyz』,請往下看。

第一步:執行a = 『abc』,直譯器建立了字串』abc』和變數a,並把a指向』abc』

第二步:執行b = a,直譯器建立了變數b,並把b指向a指向的字串』abc』

第三步:執行a = 『xyz』,直譯器建立了字串』xyz』,並把a的指向改為』xyz』,但b並沒有更改

相信你對python變數有所理解了,繼續往下看。

為什麼寫這篇部落格,因為刷leetcode出現了乙個這個現象,請看:

what???為啥list_b也跟著變了???

其實,這種情況下,list_b和list_a是一樣的,他們指向同一片記憶體,list_b不過是list_a的別名,是引用。 我們可以使用id(x) 來檢視兩個list的位址。

綜上所述,賦值操作(包括物件作為引數、返回值)不會開闢新的記憶體空間,它只是複製了新物件的引用。也就是說,除了list_b這個名字以外,沒有其它的記憶體開銷。修改了list_a,就影響了list_b;同理,修改了list_b就影響了list_a。注意:與1. 變數中例子區別開來,那個例子是新生成乙個字串,而這個是在原list_a上做修改。

你可能會問,那我想建立新物件,但要保留原物件內容應該怎麼辦呢?淺拷貝想你所想。

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

淺拷貝產生的list_b不再是list_a了,使用id檢視,發現它們不指向同一片記憶體。但是當我們使用 id(x) for x in list_a 和 id(x) for x in list_b 時,可以看到二者包含的元素的位址是相同的。

在這種情況下,list_a和list_b是不同的物件,修改list_b理論上不會影響list_a。

注意:淺拷貝之所以稱為淺拷貝,是它僅僅只拷貝了一層,在list_a中有乙個巢狀的list,如果我們修改了它,情況就不一樣了。

what???list_b變了??是的!修改列表外巢狀,list_a會修改它的引用,指向別的位置;但修改內巢狀,列表的位址並未發生變化,不會指向別的位置,在原記憶體位址修改。粗暴來說就是:修改一層不會影響,修改多層就會影響,因為淺拷貝只拷貝了一層(即拷貝了一層引用),內巢狀的引用還是共享的。

這就是我當初遇到問題的原因,原題

class

solution

:def

uniquepathswithobstacles

(self, obstaclegrid)

:"""

:type obstaclegrid: list[list[int]]

:rtype: int

"""m =

len(obstaclegrid)

n =len(obstaclegrid[0]

)# dp二維陣列定義有問題

# 這m個列表就是淺拷貝,修改任意乙個其他的也會改變

dp =[[

0]* n]

* m # 正確定義方式

# dp = [[0 for j in range(n)] for i in range(m)]

if obstaclegrid[0]

[0]==

1:dp[0]

[0]=

0else

: dp[0]

[0]=

1for i in

range(1

, m)

:if obstaclegrid[i][0

]==1:

dp[i][0

]=0else

: dp[i][0

]= dp[i -1]

[0]for i in

range(1

, n)

:if obstaclegrid[0]

[i]==1:

dp[0]

[i]=

0else

: dp[0]

[i]= dp[0]

[i -1]

for i in

range(1

, m)

:for j in

range(1

, n)

:if obstaclegrid[i]

[j]==1:

dp[i]

[j]=

0else

: dp[i]

[j]= dp[i -1]

[j]+ dp[i]

[j -1]

return dp[m -1]

[n -

1]

深拷貝只有一種形式,copy模組中的deepcopy函式。

和淺拷貝對應,深拷貝拷貝了物件的所有元素,包括多層巢狀的元素。因而,它的時間和空間開銷要高。

同樣對list_a,若使用list_b = copy.deepcopy(list_a),再修改list_b將不會影響到list_a了。即使巢狀的列表具有更深的層次,也不會產生任何影響,因為深拷貝出來的物件根本就是乙個全新的物件,不再與原來的物件有任何關聯。

python列表操作 賦值

1 元素賦值 x 1,1,1 x 1 2 print x 1,2,1 2 分片賦值 a 1,2,3,4,5 a 1 4 a print a a 1 3 print a 1,a 5 1 3 賦空值 x none 3 print x len x none,none,none out 217 3 問題 互...

從Hello World 談Python執行原理

因公司需求,今天開始學習python,對於python語言,早聽行內朋友聽過,簡單說是物件導向的簡單輕巧 解釋性的指令碼語言。通過今天寫hello world確實領教了python的簡潔。一 hello world的python兩種實現方法 方法一 開啟python的idle python gui ...

python 函式中的列表賦值

在python中,將列表傳遞給函式,在函式中對列表進行任何修改都是永久性的。但是如果在函式中對傳遞來的列表進行賦值操作,則不會影響列表的資料。這樣一段 def merge sort arr arr 4,6 arr 6,4,8,1,4,2,7,6 merge sort arr print arr 其輸...