python list的深拷貝與淺拷貝-以及初始化空白list的方法(1)


 

在一次做題的時候遇到了一件令人非常匪夷所思的“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)]

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM