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
- https://docs.python.org/3/reference/compound_stmts.html#function-definitions
- https://stackoverflow.com/questions/1132941/least-astonishment-and-the-mutable-default-argument