1. 程式人生 > >決戰Python之巔(六)---補充篇

決戰Python之巔(六)---補充篇

前言

本來是想在上一篇最後直接加的,但是覺得接著寫篇幅可能會比較長,可能你們就翻不到那邊,所以決定重開一篇,來好好寫寫list的深淺Copy。(我也是剛看完視訊,趁熱打鐵來複盤一下)。
敲黑板!!!好好看!!有點繞,但是看懂了就懂了,不懂的可以評論提問。

知識回顧

首先,先看一段程式碼:

a = 1
b = a

問題1:b = ? 很簡單,答案是1。
那麼,這樣呢?

a = 1
b = a
a = 2

問題2:請問現在b = ? 也很簡單,b還是等於1。為什麼呢?不是說b = a了嘛?既然a改了b為什麼沒改呢?
請看下面:
在這裡插入圖片描述
當執行完 b = a後,大家可以發現,a 和 b的記憶體地址是一樣,也就是說a和b都指向了140716819469344這個記憶體地址。那麼當我對a重新賦值時,大家可以看到:
在這裡插入圖片描述


a指向的記憶體地址改變了,而b還是指向原來的記憶體地址。也就是說,除非你重新給b賦值或者再執行一次 b = a ,否則b指向的記憶體地址就會一直是原來的,無論a變成什麼,都不會影響到b。這麼說可能有點幹…畫個圖就知道了。
一開始變數a指向的是1的記憶體地址,
在這裡插入圖片描述
執行完b = a 後,a和b都指向了這個記憶體地址:
在這裡插入圖片描述
接下來 a = 2:
在這裡插入圖片描述
a就指向了另一個記憶體地址,而b不變。
這是前提。
接下來講list,首先還是一樣,初始化兩個變數:
在這裡插入圖片描述
毋庸置疑,L2 肯定是 [1,2,3,4,5,6],對吧。好,那接下來,我要將L1的第一個元素換成0:
在這裡插入圖片描述
那麼,請問,現在L2是多少呢?根據我上面講的,照道理應該還是[1,2,3,4,5,6],那真的是這樣的嗎?我們試驗一下:
在這裡插入圖片描述

What???為什麼L2也改變了?Why??
回答這個問題之前,照例我們看一下兩個list的記憶體地址:
在這裡插入圖片描述
list的記憶體地址還是一樣,沒什麼問題,那麼,改了L1之後的記憶體地址呢?
在這裡插入圖片描述
????為什麼我改了之後L1的記憶體地址沒有變呢?
首先大家要知道的是,列表裡每一個元素都是一個獨立的個體,它們都有自己的記憶體地址:
在這裡插入圖片描述
這就相當於,唔…我用Alex的例子來解釋:list就相當於一個容器,假設就是杯子吧,元素是獨立的個體,就把它們當做是棗子吧,雖然把棗子放進了杯子,但他們還是獨立存在的,也就是說每一個棗子,啊呸,每一個元素都有獨立的記憶體地址(從上圖我們也可以看得出來),其次杯子本身也是有記憶體地址。現在我們初始化了一個list為L1,它有一個記憶體地址為ID1,然後又重新初始化了一個list為L2,執行L2 = L1,那麼可以知道L2指向的是L1實際的記憶體地址即ID1,它只是指向了這個list,並沒有指向裡面的元素。
在這裡插入圖片描述

在這裡插入圖片描述
如果從裡面拿走一個元素,或者新增一個元素,那麼L1,L2都會改變。實質上就是L1和L2都指向了一個杯子,往杯子里加一個棗或者減一個棗或者改變一個棗那麼L1和L2裡的元素都會改變,但是這個杯子還是這個杯子,就像你大爺還是你大爺,沒變。這樣應該能懂了吧。
然後是Copy, copy的意思就是複製。也就是說現在我有一個杯子L1,然後執行L2 = L1.copy(),就相當於我複製了一個杯子給L2,那麼L1和L2現在是兩個獨立的杯子,也就是說他們指向了兩個不同的記憶體地址:
在這裡插入圖片描述
現在我再將L1的第一個元素改變,L2還會跟著改變嗎?
在這裡插入圖片描述
可以看到,現在就不會都改變了。你以為就這麼簡單??NO NO NO。我們再來看一下Copy過後list裡元素的記憶體地址:
在這裡插入圖片描述
為啥這兩個元素的記憶體地址是一樣的捏?事實上,執行copy操作之後,你只將杯子複製了一個,但是杯子裡的棗是兩個杯子共享的,元素並沒有copy。那麼問題來了,既然是共享,那為什麼L1改了之後,L2沒變呢?我用一張圖就能明白了:
在這裡插入圖片描述
很眼熟對吧,一開始L1[0]和L2[0]都指向了0(因為copy的原因),然後L1[0]改變了指向了999,但L2[0]指向的記憶體地址還是0,所以沒有跟著改變。這就和a = 1,b = a, a = 2, b還是等於1一樣,只不過b = a這句話的效果我們用copy完成了。

到這裡…還沒結束,給大家一個問題,大家思考下:
在這裡插入圖片描述
這裡L2是多少呢?
在這裡插入圖片描述
那這裡的L2又是多少呢?(敲一遍程式碼得到結果當然容易,但最好還是自己思考下為什麼?)

----------------------------------------------------------預設你已經思考完了-------------------------------------------------------------------------------
第二次執行的結果,很意外,
在這裡插入圖片描述
L2竟然跟著L1變了,why???我們還是先看下記憶體地址:
在這裡插入圖片描述
2410125909896這個記憶體地址實際上指的是裡面那個小列表的記憶體地址,就相當於大杯子裡的一個小杯子的記憶體地址,二小杯子裡的元素的記憶體地址還是獨立的,這裡就是我上面講的那些,不懂的話可以翻回去看。也就是說,如果一個list中的某些元素也是list(列表是可以巢狀的),那麼執行copy的話,也只是將小杯子的記憶體地址copy了一遍,他們都指向了同一個小杯子,裡面的元素是共享的,所以前面的改了後面的也跟著變了,能理解了吧。
那如果,我現在要完完全全獨立,裡面的子列表我也想完全獨立,現在這樣的淺層copy就沒辦法實現。怎麼辦呢?既然有淺層的copy,那麼肯定也會有深層次的copy啦:
在這裡插入圖片描述
這就相當於,現在我除了有兩個大杯子,還有兩個小杯子,而不再是兩個大杯子都用一個小杯子。現在L1和L2是完全獨立的,更改任何一個都不會影響到另一個。但是這個方法極少用,也勸大家不要用,為啥呢?假如說我現在有一個list,很大,大概有10個G,現在你deepcopy一下,這就有兩個10G的list,也就是20G,非常佔容量,所以還是少用為妙,除非迫不得已。