2012-04-20 17 views
0

可能重複:
「Least Astonishment」 in Python: The Mutable Default Argumentpython 2.7中的staticmethod參數保持跨呼叫值?

使用Python 2.7我遇到奇怪的行爲來了,我不知道該如何解釋,或者如果它甚至在任何python文檔存在。使用代碼

class MyClass: 
@staticmethod 
def func(objects=[], a=None, b=None): 
    objects.append(a) 
    print 'objects: %s'%objects 
    print 'b: %s'%b 


MyClass.func(a='one') 
MyClass.func(a='two', b='foo') 
MyClass.func(a='three') 

我得到得到輸出

objects: ['one'] 
b: None 
objects: ['one', 'two'] 
b: foo 
objects: ['one', 'two', 'three'] 
b: None 

正如你可以看到,第一個列表參數(對象)的方法,保留它在調用值..新的值被附加到即使在它的頭部聲明中它有一個默認值[]。但最後一個參數(b)不保留它的值,它被重置爲調用之間的默認值。

預期(對我來說)是對象參數應該重置爲任何方法調用時的默認值(比如b參數),但這似乎不會發生,似乎只發生在第一個電話。

任何人都可以解釋這種行爲嗎?它是這個版本的Python的bug還是它的行爲?可能與列表引用在呼叫中保留有關,但字符串變量(b)不是?我很困惑這種行爲。

謝謝

+1

0123sthttp://stackoverflow.com/questions/1132941/least -python-the-mutable-default-argument – mayhewr 2012-04-20 00:31:24

+0

謝謝,這回答了我的問題。良好的聯繫,解釋這一切,從該鏈接的答案http://effbot.org/zone/default-values.htm – 2012-04-20 00:56:01

回答

3

它與靜態方法無關。這是Python中一個非常常見的錯誤。

Python中的函數是第一類對象,而不僅僅是代碼塊,因此您分配給參數中對象的空列表是附加到函數對象的真實列表。每次你打電話給它並追加一些東西到你的列表中時,你都會使用相同的列表。人們很容易看到它的發生是這樣的:

>>> def func(x, objects=[]): 
...  objects.append(x) 
... 
>>> func(1) 
>>> func.func_defaults 
([1],) 
>>> func(2) 
>>> func.func_defaults 
([1, 2],) 
>>> func(3) 
>>> func.func_defaults 
([1, 2, 3],) 

func_defaults是constains你設置的默認關鍵字參數的函數對象的屬性。看看清單是如何變化的?

正確的方法做你想做的是:

class MyClass: 
@staticmethod 
def func(objects=None, a=None, b=None): 
    if objects is None: 
     objects = [] 
    objects.append(a) 
    print 'objects: %s'%objects 
    print 'b: %s'%b 
+0

我從來沒有見過func_defaults之前。是否在任何地方記錄爲Python API的一部分? – mgilson 2012-04-20 01:22:54

+0

它可能沒有記錄在任何地方,像大多數內部實現細節。它確實不應該用於任何嚴肅的事情,其他Python實現可能不符合它。 – 2012-04-20 01:31:10

+0

我想知道它是否是一個實現細節......還是很有趣的。 – mgilson 2012-04-20 01:39:03

1

此行爲是廣爲人知的,被許多的功能處理,而不是靜態方法特異性。它適用於每個功能。 將可變對象作爲默認值分配給參數時發生。

見StackOverflow上這個答案:Default Argument Gotchas/Dangers of Mutable Default arguments

如果你理解與可變性不變性問題,想到的功能/方法類似的:

  • 它被定義的情況下,默認參數值被分配,
  • 被調用時,執行主體,並且如果它改變可變默認參數值然後在每次後續呼叫中使用更改後的值,其中默認參數值未被提供不同的值而被覆蓋,而不是通過提供不同值而被覆蓋,