攝影或3C

Python 新手必看陷阱:為什麼我的預設 List 越來越長?(Mutable Default Argument 解析)

你在寫 Python 函式時,是不是曾經寫過類似這樣的程式碼,想說「如果呼叫時沒有傳入清單,就預設給一個空的 `[]`」?

def append_to_list(value, my_list=[]):
    my_list.append(value)
    return my_list

看起來非常直覺對吧?但當我們實際執行幾次時,卻發生了恐怖的事情:

print('第一次呼叫:', append_to_list(1))
print('第二次呼叫:', append_to_list(2))
print('第三次呼叫:', append_to_list(3))

**【預期結果】**

第一次呼叫: [1]
第二次呼叫: [2]  <-- 因為沒有傳 my_list原本以為會重新拿到 []
第三次呼叫: [3]

**【實際結果】**

第一次呼叫: [1]
第二次呼叫: [1, 2]
第三次呼叫: [1, 2, 3]  <-- 什麼?!為什麼前面的資料還在

這究竟是怎麼回事?難道是 Python 當機了嗎?

其實,這是 Python 裡最經典也最常考的面試題之一:
**「可變預設參數陷阱 (Mutable Default Argument)」**

## 🔍 兇手抓到了:預設值只在「函式定義」時建立一次!

要理解這個現象,我們必須知道 Python 直譯器 (Interpreter) 運作的一個重要機制:

> **函式的預設值 (Default Arguments),是在 Python 讀到 `def` 這行程式「定義函式時」被建立並綁定的,而不是「每次呼叫函式時」重新建立!**

讓我們用慢動作重播剛剛發生了什麼事:

1. **定義期 (Definition Time):**

   當 Python 讀到 `def append_to_list(value, my_list=[]):` 時,它在記憶體中(假設地址是 `0x1000`)建立了一個空清單 `[]`,並且把它掛在 `append_to_list` 這個函式的身上當作預設值。

2. **第一次呼叫 – `append_to_list(1)`:**

   你沒有傳入 `my_list`,所以 Python 拿出了 `0x1000` 這個清單。

   執行 `.append(1)` 後,`0x1000` 變成了 `[1]`。

3. **第二次呼叫 – `append_to_list(2)`:**

   注意這裡!因為預設值並不會重新計算,Python **再次拿出了同一個** `0x1000` 的清單。

   但此時它裡面已經裝了 `[1]` 啦!

   執行 `.append(2)` 後,它理所當然變成了 `[1, 2]`。

因為 List (清單) 和 Dictionary (字典) 在 Python 中都是 **可變物件 (Mutable)**,只要你在函式內修改了它,這個修改就會永遠留存在函式身上,汙染了後續所有的呼叫。

## 🛠️ 解決方案:標準防呆寫法

知道原因後,解法其實很簡單:

**永遠不要把可變物件 (如 `[]`, `{}`) 寫在預設值裡。改用不可變的 `None` 取代!**

標準的正確寫法如下:

def append_to_list(value, my_list=None):
    # 每次呼叫時才作檢查動態創造全新的清單
    if my_list is None:
        my_list = []  
        
    my_list.append(value)
    return my_list

我們再執行一次看看:

### 為什麼換成 `None` 就沒事了?

因為 `None` 是一個**不可變物件 (Immutable)**。每當你呼叫函式而沒有傳入 `my_list` 時,變數會先被指派為 `None`,然後進到 `if` 判斷式中。此時執行的 `my_list = []`,是在**函式呼叫的當下**「全新產生」的獨立清單,它們彼此擁有獨立的記憶體位置,再也不會互相干擾了。

## 💡 總結

下次寫 Python 函式時,請在心裡默念:

1. **預設參數只會被評估一次。**

2. **看到 `def fn(data=[])``def fn(data={})`,你的雷達就要響起!**

3. **一律改寫成 `def fn(data=None)`。**

這不僅能避免靈異現象般的 Bug,
也能讓你的程式碼看起來更專業、
更符合 Python 的標準規範 (Idiomatic Python) 喔!

推薦hahow線上學習python: https://igrape.net/30afN

儲蓄保險王

儲蓄險是板主最喜愛的儲蓄工具,最喜愛的投資理財工具則是ETF,最喜愛的省錢工具則是信用卡