2012-11-23 101 views
2

可能重複:
「Least Astonishment」 in Python: The Mutable Default Argument變量範圍在單元測試蟒蛇

我看到試圖運行一些單元測試相關變量的作用域一些奇怪的行爲(蟒蛇2.7):

我有一個模塊,其中包含一個名爲'Dummy'的類,它有一個列表作爲實例變量。在我爲這個模塊進行的單元測試中,我有兩個獨立的測試用例,每個測試用例都創建了自己獨立的Dummy實例。儘管如此,測試中運行的Dummy實例似乎包含在第一個測試中創建的列表。下面的代碼:

mymodule.py:

class Dummy(object): 
    def __init__(self, mylist=[]): 
     self.mylist = mylist 

test_mymodule.py:

class TestDummy(unittest.TestCase): 
    def testDummy1(self): 
     d1 = Dummy()   # d1.mylist is empty, [] 
     d1.mylist.append(1) # mylist = [1] 
     self.assertEqual(d1.mylist, [1]) 

    def testDummy2(self): 
     d2 = Dummy()   # d2.mylist = [1] ! 
     d2.mylist.append(2) # d2.mylist = [1,2] 
     self.assertEqual(d2.mylist, [2]) # FAILS 

其他細節:

  1. 如果我把一個print mylist作爲第一線Dummy's init方法,果然,在seco上nd測試運行時,它會打印[1],好像mylist = [1]作爲參數傳入

  2. 如果我明確指定dd = Dummy(mylist=[]),則問題消失。這似乎與默認的參數值有關,但爲什麼在任何情況下它應該蔓延到另一個範圍內對我來說還不清楚。

+3

http://docs.python.org/2/faq/design.html#why-are- default-values-shared-between-objects –

+0

@MarkDickinson這對我來說似乎不同,因爲我沒有像在這個例子中那樣在函數內設置參數的值。但我認爲發生的事情是,一旦實例變量設置爲等於__init__中的參數,因爲它是通過引用的方式,當d1.mylist在原始範圍中更改時,實際上它將更改默認參數的值。您的鏈接中描述了哪個問題。這聽起來適合你嗎? – mindthief

+0

@mindthief在你的情況下,創建類時創建默認參數。因此,使用默認值的所有實例實際上共享相同的列表。永遠不要使用可變類型作爲默認值。如果你想要一個空的迭代而不是'None',那麼使用一個空元組''()'。 – Bakuriu

回答

2

這與單元測試無關,而是與Python如何處理函數的默認參數有關。當執行def語句時,函數默認參數會被計算一次。

這意味着您已經創建了一個使用__init__的默認參數的列表,並且只要該函數存在,該列表就會存在。

這是很容易避免由使用None(或另一定點值)作爲默認:

class Dummy(object): 
    def __init__(self, mylist=None): 
     self.mylist = mylist if mylist is not None else [] 
+0

謝謝,這似乎是正確的答案(同時在問題的評論中同時討論) – mindthief