python預設參數陷阱 0|1陷阱? 學過函數的人一定聽說過函數的預設參數,關於函數的預設參數,請看以下的例子: def extendList(val, lst=[]): lst.append(val) return lst list1 = extendList(10) list2 = exten ...
python預設參數陷阱
0|1陷阱?
學過函數的人一定聽說過函數的預設參數,關於函數的預設參數,請看以下的例子:
def extendList(val, lst=[]): lst.append(val) return lst list1 = extendList(10) list2 = extendList(123, []) print('list1 = %s' % list1) print('list2 = %s' % list2)
列印的結果是 現在,我們將代碼再添加一處,來看看最後的結果是什麼:
def extendList(val, lst=[]): lst.append(val) return lst list1 = extendList(10) list2 = extendList(123, []) list3 = extendList('a') print('list1=%s' % list1) print('list2=%s' % list2) print('list3=%s' % list3)
當list1處調用函數時,10被加入了列表;list2處調用函數,123被加入到了新傳入的列表中;最後到list3調用函數,應該將‘a’繼續加入到列表中返回。因此得到的輸出應該是:
# list1 = [10] # list2 = [233] # list3 = ['a']
0|1陷阱!
然而,實際的列印結果變成了:
陷阱之所以稱之為陷阱,代表我們不能以普通的思維來看待它,通過查閱資料,得到以下的一句解釋:
A new list is created once when the function is defined, and the same list is used in each successive call.
在定義函數時,Python的預設參數會被計算一次,而不是每次調用函數時(比如Ruby)。這意味著如果你使用一個可變的預設參數並對其進行改變,那麼你將會直接修改該對象,該影響將一直延續到未來關於該函數的調用(在預設參數沒有被重新賦其他值的情況下)。
眾所周知,Python變數存儲的是變數和值的引用關係,即實際變數對應一個記憶體地址。這意味著Python函數總是通過地址傳遞(傳遞參數)工作。調用函數時,不會將參數值複製到函數占位符。相反,我們將占位符指向變數本身。這有一個非常重要的結果:我們可以從函數內部更改變數的值。
0|1如何避開陷阱?
None通常是一個不錯的選擇:
def extendList(val, lst = None): if not lst: lst = [] lst.append(val) return lst
有時您可以專門利用此陷阱來維護函數調用之間的狀態。這通常在編寫緩存函數時完成。
參考資料:https://docs.python-guide.org/writing/gotchas/
http://blog.thedigitalcatonline.com/blog/2015/02/11/default-arguments-in-python/