Skip to content

[Python] 函式使用可變物件當作參數預設值存在的問題

Last Updated on 2022-04-18 by Clay

在建立 Python 函式的過程中,有時我們會傳入參數讓建立的函式去處理,或許我們還會設定該參數的預設值,讓參數在我們沒有傳入任何值的情況下,仍能以一個預設(default)值去執行函式。

不過當我們在使用『可變物件』(比方說 List 或 Dict 等資料)當作參數預設值時,有可能會在比較難注意到的地方引起不必要的錯誤。


可能引發的錯誤範例

以下是一個經典的把可變物件當作參數預設值所引發的錯誤:

def foo(arr=[]):
    arr.append(100)

    return arr


def main():
    print(foo())
    print(foo())
    print(foo())


if __name__ == "__main__":
    main()


理想中我希望不管執行了 foo() 函式多少次,我始終都能得到 [100] 這樣的回傳值,也就是說我們希望得到:

[100]
[100]
[100]


然而實際上,我們所得到的回傳列表可能會出乎我們意料。真正的回傳值是:

[100]
[100, 100]
[100, 100, 100]


而這種跟我們預期中不同的回傳值,就會是程式可能潛在的錯誤。

那麼,首先來講講為什麼會這樣吧!

簡單來說,如果閱讀過官方文件(可以參閱文末 References 的段落),我們對 def 關鍵字的理解就是會創造一個函式物件,然後只會在建立時建立初始化參數一次,接著不論我們調用了多少次該函式,函式的初始化物件都會是同樣的物件。

所以,如果我們設定了一個可變物件的預設值,我們其實就會在每次呼叫函式時,操作同樣一個可變物件


解決方法

最單純、最直接的解決方法就是,我們不要把參數預設值設定成可變物件。比方說,我們真正希望達到的函式效果,大概就如同下方程式:

def foo(arr=None):
    if arr is None:
        arr = []

    arr.append(100)

    return arr


def main():
    print(foo())
    print(foo())
    print(foo())


if __name__ == "__main__":
    main()


Output:

[100]
[100]
[100]

References


Read More

Tags:

Leave a Reply