在Python教程裏,針對默認參數,給了一個「重要警告」的例子:javascript
def f(a, L=[]): L.append(a) return L print(f(1)) print(f(2)) print(f(3))
默認值只會執行一次,也沒說緣由。會打印出結果:java
[1] [1, 2] [1, 2, 3]
由於學的第一門語言是Ruby,因此感受有些奇怪。 但確定的是方法f必定儲存了變量L。python
p指向不可變對象,好比數字。則至關於p指針指向了不一樣的內存地址。git
p指向的是可變對象,好比list。list自身的改變,並不會改變list對象自身所在的內存地址。因此p指向的內存地址不變。github
>>> p = 1 >>> id(p) 4523801232 >>> p = p + 1 >>> id(p) 4523801264 >>> p = 11 >>> id(p) 4523801552 >>> p = [] >>> id(p) 4527080640 >>> p.append(11) >>> id(p) 4527080640
Python函數的參數默認值,是在編譯階段就綁定了。(寫代碼時就定義了。)app
下面是一段從Python Common Gotchas中摘錄的緣由解釋:ide
Python’s default arguments are evaluated once when the function is defined, not each time the function is called (like it is in say, Ruby). This means that if you use a mutable default argument and mutate it, you will and have mutated that object for all future calls to the function as well.函數
由此可知:ui
第三條,修改上面的例子:lua
def f(a, L = 1): L = a print(id(L)) return L print("self",id(f.__defaults__[0])) print(f(1)) print("self",id(f.__defaults__[0])) print(f(33)) print("self",id(f.__defaults__[0])) #運行結果: self 4353170064 4353170064 1 self 4353170064 4353171088 33 self 4353170064
默認參數L,在編譯階段就綁定了,儲存在__default__內。函數體內的L = a表達式,生成的是新的變量。返回的L是新的變量,和默認參數無關。
第四條,仍是上面的例子, 改一下默認參數的類型爲可變對象list:
def f(a, L = []): L.append(a) print(id(L)) return L # L = f(1) print("self",id(f.__defaults__[0])) print(f(1)) print("self",id(f.__defaults__[0])) print(f(33)) print("self",id(f.__defaults__[0]))
#返回結果 self 4337586048 4337586048 [1] self 4337586048 4337586048 [1, 33] self 4337586048
由id號可知,返回的是默認參數自身。
def f(a, L = None): if L is None: L = [] L.append(a) return L
StackOverflow 上爭論不少。
Ruby之因此沒有這個問題,我想是由於Ruby的def關鍵字定義了一個封閉做用域,任何數據都必須經過參數傳入到方法內,才能用。
和Ruby比,Python參數的做用被大大消弱了。Python的出現晚於Ruby,其創始人確定參考了Ruby的設計。拋棄了這個設計,選擇了相似javascript的函數方式。def定義的函數,執行時是能夠接收外部做用域的變量的。
有觀點認爲:
出於Python編譯器的實現方式考慮,函數是一個內部一級對象。而參數默認值是這個對象的屬性。在其餘任何語言中,對象屬性都是在對象建立時作綁定的。所以,函數參數默認值在編譯時綁定也就不足爲奇了。
本文參考了:http://cenalulu.github.io/python/default-mutable-arguments/#toc1 ,並加入了本身的理解。歡迎轉載!