理解 Python 函數中的預設值機制

更新日期: 2025 年 1 月 23 日

在 Python 中,函數的參數可以指定預設值,這讓函數更靈活易用。

然而,當預設值為可變物件(如串列或字典)時,可能會導致意外的行為。

本文將詳細解析這一現象,並提供正確的使用方法。


函數預設值的機制

Python 的函數預設值是在函數定義時計算並儲存的,且該值在後續的函數調用中會被重複使用。

如果預設值是一個可變物件(如串列、字典),則對該物件的修改會影響後續調用。

範例:預設值為串列的問題

def hi(a, num=[]):  # 預設值 num 是一個空串列
    num.append(a)
    print(num)

hi(1)  # 輸出 [1]
hi(2)  # 輸出 [1, 2]
hi(3)  # 輸出 [1, 2, 3]

行為分析

預期輸出應為:

[1]
[2]
[3]

但實際輸出卻是:

[1]
[1, 2]
[1, 2, 3]

原因是 num 的預設值 [] 在函數定義時被初始化,並在後續調用中共享同一個物件。

每次調用函數時,num.append(a) 都會修改同一個串列。

機制解析

函數的預設值是在定義階段,儲存在記憶體中的。

當使用預設值時,函數調用不會重新初始化該值,而是引用已有的物件。

流程圖解釋

函數定義階段:
num --> 空串列 []

第一次調用 hi(1)
num 引用 --> [1]

第二次調用 hi(2)
num 引用 --> [1, 2]

第三次調用 hi(3)
num 引用 --> [1, 2, 3]

解決方法

可以將預設值設為 None,在函數內部進行檢查並初始化:

def hi(a, num=None):
    if num is None:
        num = []  # 每次都創建一個新串列
    num.append(a)
    print(num)

hi(1)  # 輸出 [1]
hi(2)  # 輸出 [2]
hi(3)  # 輸出 [3]

範例 2:預設值為隨機數的問題

import random

def hi(n, m=random.randint(1, 100)):  # 預設值 m 是隨機數
    print(n + m)

hi(1)
hi(1)
hi(1)

行為分析

預期輸出應該是每次 m 都是新的隨機值,但實際輸出如下:

93 (一個隨機數字)
93
93

原因解析

與串列的情況類似,m 的預設值在函數定義階段已經計算並儲存,後續調用使用的都是同一個固定的值。

解決方案

與前述方法一致,可將預設值設為 None,在函數內部進行初始化:

import random

def hi(n, m=None):
    if m is None:
        m = random.randint(1, 100)  # 每次調用重新生成隨機數
    print(n + m)

hi(1)  # 隨機輸出,如 42
hi(1)  # 隨機輸出,如 57
hi(1)  # 隨機輸出,如 89

注意事項與建議

  1. 避免可變物件作為預設值:串列、字典等可變物件應該避免直接作為函數參數的預設值。
  2. 使用 None 作為哨兵值:將預設值設為 None,在函數內部進行初始化,確保每次調用不共享同一物件。
  3. 對函數的預設值保持謹慎:特別是在涉及隨機數或需要獨立記憶體空間的場景中,務必檢查預設值的行為。

結語

Python 函數的預設值機制雖然靈活,但其特性可能導致意外的行為,尤其是當預設值為可變物件時。

理解預設值的初始化時機以及共享記憶體的特性,能幫助開發者避免相關錯誤。

在實際使用中,推薦使用 None 作為哨兵值,結合內部初始化邏輯,確保函數行為的正確性與可預測性。

Similar Posts