在一次做題的時候遇到了一件令人非常匪夷所思的“bug”:我想要做的事情是,初始化shape確定,但值為“空”的list,並且是一個二維的list,開始我是這么做的:
l1=[[""]*3]*3 print l1 [['', '', ''], ['', '', ''], ['', '', '']]
可以看到這個矩陣的shape,可以理解成一個N*N的矩陣。
把題目也貼出來吧——題目要求很簡單,輸入一個N*N的矩陣,輸出這個矩陣的順時針旋轉90°之后的矩陣。一開始我的代碼如下:
class Rotate: def rotateMatrix(self, mat, n): # write code here v=[[""]*3]*3 for i in range(n): for j in range(n): v[i][j]=mat[n-j-1][i] return v
輸入的測試數據為:
[[1,2,3],[4,5,6],[7,8,9]]
正確的輸出應該為:
[[7,4,1],[8,5,2],[9,6,3]]
而我的運行結果卻是:
[[9,6,3],[9,6,3],[9,6,3]]
非常不解,輸出的三個子數組居然是一樣的結果
后來對原來的代碼略加修改,把“空”list的初始化定義為:v=[[
0
for
i in range(n)]
for
j in range(n)],即:
class Rotate: def rotateMatrix(self, mat, n): # write code here v=[[0 for i in range(n)] for j in range(n)] for i in range(n): for j in range(n): v[i][j]=mat[n-j-1][i] return v
此時終於輸出了正確的結果[[9,6,3],[9,6,3],[9,6,3]],代碼測試通過。
這里對比一下這兩種初始化方法:
至少在輸出結果看,長的是一模一樣,難道這里的l1和l2真的沒有區別嗎?這里再介紹一個常用函數------id(x),它表示括號內變量所在的內存地址
我們分別來看一下l1,l2內子list的內存地址情況:
震驚!這里可以看到,l1的三個子list的內存地址居然一模一樣!而l2的子list所在的內存地址各不相同!??
我們隊l1,l2的子list分別進行extend操作,如下:
我們本來只是對l1/l2的第二個子list進行extend操作,但結果卻是l1的三個子數組都受到了影響,而l2是只有第二個子數組實現了extend操作。
再分別看此時各子list的內存地址:
與剛才的結果一致,l1的三個子數組位於同一段內存地址,l2則各不相同。
用個直接使用“=”對l1的子list進行賦值呢?
此時,只有被賦值的l1[1]成功賦值,另外兩個沒有變,並且l1[1]的內存地址已經發生變化,而l1[0],l1[2]依然不變
至此可以總結一下原因了:
1.使用*進行list“復制”,其生成的新數組與被復制的數組其實是在同一段內存地址當中,這樣的復制方式成為潛復制
2.淺復制進行初始化的結果就是,在對復制之后的對象進行相關操作時,被復制的對象會受到同樣的影響,因為他們本質是同一段list,均位於相同的地址
3.使用“=”進行賦值,是將"="后的內容創建到一個新的內存地址當中,並將"="左的變量的地址重新指向"="右產生的新地址當中
具體到解決實際問題,在初始化一個"已知shape,但內部的值待填充"的一個"空"list時,建議使用for循環進行初始化,例如上文提到這個格式,即:
v=[['' for i in range(m)] for j in range(n)]