2013-04-20 84 views
4

我是Python新手,在使用它編寫小應用程序時,遇到了一個問題。由於我在Google上找不到任何類似的東西,我可能會誤解Python的一些基本概念。如果你們中的某個人能向我解釋這一點,那將是非常棒的。重構用於新對象的構造函數參數?

所以這裏是一個最小的工作示例。請忽略類和變量名稱,這只是一個人爲的例子,但它顯示了我的意思。你可以將它寫入你的IDE併爲你自己運行。

class State(object): 
    def __init__(self, wrappers = []): 
     self.wrappers = wrappers 

    def add(self, name, items): 
     lst = KeyValList(name) 
     for item in items: 
      lst.add(item, items[item]) 
     self.wrappers.append(Wrapper(lst)) 

class Wrapper(object): 
    def __init__(self, kv_lst): 
     self.kv_lst = kv_lst 

class KeyValList(object): 
    def __init__(self, name, kvals = []): 
     self.name = str(name) 
     self.kvals = kvals 

    def add(self, key, value): 
     self.kvals.append(KeyVal(key, value)) 

class KeyVal(object): 
    def __init__(self, key, val): 
     self.key = str(key) 
     self.val = val 

好的,那就是我使用的類的定義。
右下面我寫了以下內容:

state = State() 
kv = { "key1" : "1", "key2" : "2", "key3" : "3" } 
state.add("1st", kv) 
kv = { "keyX" : "X", "keyY" : "Y", "keyZ" : "Z" } 
state.add("2nd", kv) 

for wrap in state.wrappers: 
    print wrap.kv_lst.name, "-->", 
    for kv in wrap.kv_lst.kvals: 
     print "%s:%s" % (kv.key, kv.val), 
    print "" 

作爲一個Java程序員,我會想到以下的輸出:

1st --> key3:3 key2:2 key1:1 
2nd --> keyZ:Z keyY:Y keyX:X 

然而,這一方案在Python我得到以下輸出,而不是:

1st --> key3:3 key2:2 key1:1 keyZ:Z keyY:Y keyX:X 
2nd --> key3:3 key2:2 key1:1 keyZ:Z keyY:Y keyX:X 

爲什麼兩個列表都包含所有鍵/值對?

我希望這個問題某處KeyValList類,因爲在調試時state.add()方法被調用在主程序中的第2次的KeyValList構造函數的參數kvals包含前者對。但是,這怎麼可能呢?我不提供任何東西給這個kvals參數,不應該因爲參數默認值而被初始化爲空列表嗎?

PS:我在Windows上使用Python 2.7.3。

+3

[可變默認參數](http://stackoverflow.com/questions/1132941/least-astonishment-in-python-the-mutable-default-argument) – katrielalex 2013-04-20 22:19:36

回答

1

而不是使用可變默認參數,使用沒有作爲佔位符,並讓您的函數/方法在每次調用時構造新的容器。

更改此:

class State(object): 
    def __init__(self, wrappers = []): 
     self.wrappers = wrappers 

要這樣:

class State(object): 
    def __init__(self, wrappers = None): 
     if wrappers is None: 
      wrappers = [] 
     self.wrappers = wrappers 

有問題的this blogpost一個很好的解釋。

+1

非常感謝,這對我幫助很大。您提供的鏈接也很好地解釋了「問題」。當我讀它時,我首先想到Python中的這很奇怪,但稍後會解釋爲什麼它確實有意義。謝謝。 – Matthias 2013-04-20 22:40:37

2

這基本上是一個非常常見的例子 - 而且起初頗爲違反直覺 - Python的特徵是mutable default argument。要點是缺省參數是函數對象的屬性,因此如果您在一個函數調用中對它們進行變異,則可以將它們變爲所有函數調用。

這個例子比標準的可變默認參數問題稍微複雜一點,因爲你也傳遞相同的對象作爲類的屬性和實例。但是代碼中只有一個列表對象 - 由[]創建的 - 因此通過實例屬性對其進行變異與在函數對象上對其進行變異相同。

換句話說,當你寫

self.kvals.append(...) 

要追加一些數據到一個特定列表對象。哪個列表對象?在函數的定義中由[]創建的一個,因此它與Python用作函數的默認參數的一樣。

+0

謝謝,這當然解釋了我的情況是什麼問題。我現在必須將它記入我的大腦,以便爲未來記住它:) – Matthias 2013-04-20 22:39:03