日常 Python:我們到底需不需要手動刪除不用的變數

#python
五倍技術部
技術文章
日常 Python:我們到底需不需要手動刪除不用的變數

日常 Python:我們到底需不需要手動刪除不用的變數

這幾天透過龍哥寫的為你自己學 Python 複習 Python,讀到「刪除變數」的章節時,文章開頭是這麼說:

變數宣告之後,如果不需要的話應該怎麼處理?因為「宣告」這個行為表示你要請電腦分一小塊記憶體給你存放這個變數,本著「有借有還再借不難」的好習慣,照理說如果確定之後不會再用到的話,應該要把佔用的記憶體還給系統。

讀到這邊,還先不要斷章取義,擅自決定從今天起,我要養成手動刪除變數的好習慣!我們先繼續看下去:

有些程式語言是需要手動做這件事的,但在 Python 不用我們操心,Python 有自己的資源回收機制(Garbage Collection)會幫我們搞定這些事情。

抓到重點了嗎?Python 有「資源回收機制(Garbage Collection)」。

所以乍看之下,我們似乎不用太擔心變數慢慢蠶食記憶體,不過既然如此,為什麽 Python 還需要提供 del 這個關鍵字(keyword)讓我們可以自行刪除變數呢?

是誰佔用了記憶體?

在開始解說資源回收機制(Garbage Collection)與 del 之前,我們需要先釐清一件事——「到底是變數佔用了記憶體,還是變數的值佔用了記憶體?」

實際上,佔用記憶體是變數的值,我們可以想像變數的值是一隻小貓,無論小貓叫「咪咪、小白、阿虎、肥肥」,小貓是真實存在的實體(重量可能還很重),而小貓的名字(也就是變數)只是一個參考名稱(reference),用來指向存儲於記憶體的物件,也是這隻小貓(變數的值)。

(這是一隻叫柴犬的貓,這樣他是貓還是狗?)

所以小結一下:

  • 變數:一個標籤或名稱,用於引用記憶體中的物件。
  • 值(物件):實際存儲在記憶體中的數據內容,會佔用其空間。

del 實際的作用是什麼?

為你自己學 Python 的〈刪除變數〉章節中有提到——如果你宣告了變數但因為某些原因現在就不想用了,你可以使用 Python 內建的 del 關鍵字來把變數刪掉:

柴犬 = "cat"
print(柴犬)  # 印出 "cat"

del 柴犬
print(柴犬)  # NameError: name '柴犬' is not defined.

因為刪除了 柴犬 變數,所以 柴犬 變數就變成沒有定義,如果要使用它就會出現 NameError 的錯誤訊息。

然而 del 實際上的功用,並不是將變數 柴犬 的值(也就是字串 "cat")刪除,而是將變數 柴犬 與字串 "cat" 的參考連結(reference)斷開,然後再變數名稱 柴犬 刪掉。

(斷開連結!)

換成生活化的例子,可以理解為貓("cat")還在,但這隻貓已經不叫柴犬了,所以你如果嘗試使用柴犬,也不會有貓回應你。

啊,沒有名字的貓怎麼辦?

資源回收機制登場了

接續剛剛的例子,我們可以把「沒有名字的貓」當作是流浪貓,當這隻貓一直處於流浪狀態,沒有人願意給他一個名字(變數名稱),藉此與貓產生連結的話,資源回收機制(Garbage Collection)會像動管大隊,把這隻貓帶走,從此牠就從記憶體中消失了。(怎麼有種淡淡的哀傷 ⋯⋯)

但這邊有件事沒說清楚的是——「什麼時候才會觸發資源回收機制呢?」

在 Python 中,有一種名為「參照計數(Referencing Counting)」的記憶體管理技術,它會用來追蹤物件被引用(reference)的次數,當物件的參照計數變成 0 時,Python 的垃圾回收機制會自動釋放該物件佔用的記憶體。

那什麼時候參照計數會變成 0 呢?

所有變數的引用被刪除或重新分配

當變數不再指向物件時,物件的引用計數會減少。如果沒有其他引用,計數變為 0。

a = [1, 2, 3]  # 創建列表,引用計數為 1
b = a           # 引用計數為 2
del a           # 刪除變數 a,引用計數為 1
del b           # 刪除變數 b,引用計數為 0,物件被回收

物件離開作用域

局部變數在函數執行結束後自動被釋放,引用計數變為 0。

def create_list():
    temp_list = [1, 2, 3]  # temp_list 的引用計數為 1
    return temp_list

result = create_list()  # temp_list 離開作用域後被回收,但 result 引用該物件

如果 result 沒有指向返回的物件(也就是 create_list()),則物件的引用計數會變為 0。

變數被重新分配

當變數指向新物件時,原本指向的物件的引用計數會減少。

a = [1, 2, 3]  # 創建列表,引用計數為 1
a = [4, 5, 6]  # 原物件的引用計數變為 0,內存被回收

手動使用 del 刪除變數

del 語句可以刪除對物件的引用,導致引用計數減少。

a = [1, 2, 3]  # 引用計數為 1
del a          # 引用計數變為 0,物件被回收

小結

如果要無腦地說,確實在 Python 中,我們不需要為了節省記憶體空間太過操心,資源回收機制(Garbage Collection)像是一個貼心的媽媽,看到我們不需要使用的時候,會自動幫我們收整沒在使用的物件。

不過媽媽的存在是有代價的,其中一個顯而易見的壞處是會造成性能下降,因為資源回收機制會需要定期掃描記憶體檢測無用無間,這也會進提升 CPU 額外的負擔。

如果想要更進一步瞭解 Python 記憶體資源回收機制,那就要繼續底層深掘 Python 文件跟原始程式碼了。


參考資料

如果本文章對你有幫助,歡迎按讚或者留言討論 ヽ(●´∀`●)ノ
本文同步發佈於作者的 Medium 網站