2012-07-07 35 views
0

可能重複:
「Least Astonishment」 in Python: The Mutable Default Argument終身變量的

考慮以下兩個功能

def a(var=[0]): 
    print (var) 
    var = var + [4] 

def b(var=[0]): 
    print (var) 
    var.append(4) 

他們應該採取行動一樣,但更重要的是,無論是應該只打印'[0]',如果沒有參數調用。該行爲非常不同,只有a()會一直打印'[0]'。

>>> a() 
[0] 
>>> a() 
[0] 
>>> a() 
[0] 

>>> b() 
[0] 
>>> b() 
[0, 4] 
>>> b() 
[0, 4, 4] 

爲什麼a()函數與b()不同?

好像如果你希望變量與不變量是過去的功能的情況下默認被覆蓋,你不能使用列表API。如果你做的功能將'記住'它以前的變量。

在我的代碼的情況出現在一個遞歸函數,所以只是「del'-ING不需要的變量將沒有真正的工作。每次調用沒有參數的函數時,是否有覆蓋變量的方法?

小時後和研究的時間,我發現這一點。這可能與上述問題有關,並可能導致答案。我們可以定義一輩子類,像這樣:

class lifetime: # adapted from http://naml.us/blog/tag/variable-lifetime 
    def __init__(self, name): 
     self.name = name 
     print ('creating: ' + name) 
    def __del__(self): 
     print ('destroying: ' + self.name) 
    def __iadd__(self, a): 
     self.append(a) 
    def append(self, a): 
     self.name += ' and ' + a.name 
     print('appending: ' + a.name + ' to ' + self.name) 

,然後定義2個功能:

def a(var=lifetime('a')): 
    print (var) 
    var += life('appendage') 

def b(var=lifetime('b')): 
    print (var) 
    var.append(life('appendage')) 

>>> a() 
<__main__.lifetime object at 0x00000000031FFA90> 
creating: appendage 
appending: appendage to a and appendage 
destroying: appendage 
>>> a() 
<__main__.lifetime object at 0x00000000031FFA90> 
creating: appendage 
appending: appendage to a and appendage and appendage 
destroying: appendage 
>>> b() 
<__main__.lifetime object at 0x00000000031FFB38> 
creating: appendage 
appending: appendage to b and appendage 
destroying: appendage 
>>> b() 
<__main__.lifetime object at 0x00000000031FFB38> 
creating: appendage 
appending: appendage to b and appendage and appendage 
destroying: appendage 

現在看來似乎只是將評估參數的默認一次,然後使用任何評價爲是。它從不說'創造:一個'或'創造:b'。因此,也許如果每次都評估默認參數,它會導致回答實際問題。

+0

尤見[這個答案](HTTP ://stackoverflow.com/a/10304917/577088)爲每次調用函數時創建新的可變默認值的慣用方法 - 還有[tutorial](http://docs.python.org/tutorial/) controlflow.html#默認參數的值)。 – senderle 2012-07-07 19:18:25

回答

1

因爲這兩個代碼片段做了兩件天生不同的事情。 var + [4]創建由添加的var4列表中的內容的新名單,我們然後分配這個新的列表var。另一方面,var.append(4)將數字4附加到現有列表中。

你的問題是,var=[0]只被評估一次,然後重新使用,因爲你的append更改變量本身,你會看到對後面的調用的影響。其他代碼只將一個新對象分配給一個局部變量,這個變量稍後不會生效。

通常你不使用默認值修改,而是寫:

def foo(var=None): 
    if var is None: var = [0] 

如果你需要記住的早期調用數據,把整個函數到一個類。

+0

'def b(var1 = [0]): \t from copy import copy; \t var = copy(var1); \t print(var); \t var.append(4)'也行。 – charmoniumQ 2012-07-07 21:17:21

+0

@Sam取決於它是什麼類型的變量,並非所有東西都可以被深度複製。但是,對於一個清單它會起作用 - 雖然非常不正式。 – Voo 2012-07-07 22:31:48

1

這些默認值,就像你說的,只計算一次,當函數聲明。這就是爲什麼將一個列表或散列字符分配爲默認值並不是一個好主意,因爲您只會爲所有函數調用獲取一個實例。

這一問題進行說明here

1

兩個函數的行爲不同,因爲在你第一次重新定義局部變量var[0] + [4](不附加與[0]初始化原始列表),而在第二個你實際上是追加到原來的初始化列表拿它從[0][0, 4]。正如你所提到的 - 默認值只被評估一次。

0

很簡單。在第一個函數a()中,您將將「名稱」var重新分配到包含舊的var[4]的內容的新列表。在第二個函數b()中,您正在訪問實際的var對象,而不更改名稱所指的名稱。看到這個問題作出解釋,爲什麼b()正在響應的方式是:「Least Astonishment」 in Python: The Mutable Default Argument

1

它可能會更容易想象所發生的事情,如果你把它寫像

t1 = [0] 
def a(var=t1): 
    print (var) 
    var = var + [4] 

t2 = [0] 
def b(var=t2): 
    print (var) 
    var.append(4)