2010-10-08 151 views
4

今天我打了一個有趣的python bug,其中實例化一個類重複似乎是持有狀態。在稍後的實例化調用中,變量已經定義。爲什麼通過python默認變量初始化一個變量在對象實例化中保持狀態?

我把這個問題歸結爲下面的類/ shell交互。我意識到這不是初始化類變量的最佳方式,但它肯定不應該像這樣行事。這是一個真正的錯誤還是這是一個「功能」? :d

tester.py:

 
class Tester(): 
     def __init__(self): 
       self.mydict = self.test() 

     def test(self,out={}): 
       key = "key" 
       for i in ['a','b','c','d']: 
         if key in out: 
           out[key] += ','+i 
         else: 
           out[key] = i 
       return out 

Python提示符:

 
Python 2.6.6 (r266:84292, Oct 6 2010, 00:44:09) 
[GCC 4.2.1 (Apple Inc. build 5664)] on darwin 
>>> import tester 
>>> t = tester.Tester() 
>>> print t.mydict 
{'key': 'a,b,c,d'} 
>>> t2 = tester.Tester() 
>>> print t2.mydict 
{'key': 'a,b,c,d,a,b,c,d'} 
+0

杜佩:http://stackoverflow.com/questions/1132941/least-astonishment-in-python-the-mutable-default-argument – bstpierre 2010-10-08 01:38:26

+0

莫非它將'out'解釋爲一個全局變量,因此每當你調用'test'時都會加入它? – 2010-10-08 01:27:54

+0

[「最小的驚訝」和可變的默認參數](http:// stackoverflow。com/questions/1132941 /至少令人驚訝的和可變的默認參數) – 2017-05-09 14:04:47

回答

8

這是幾乎所有Python用戶碰到一次或兩次的特徵。主要用於緩存等,以避免重複冗長的計算(真正的簡單記憶),儘管我確信人們已經找到了它的其他用途。

原因是def語句只能執行一次,也就是定義函數的時候。因此初始值只能創建一次。對於引用類型(與不可更改的不可變類型相對),就像列表或字典一樣,這最終會成爲一個可見的令人驚訝的陷阱,而對於值類型,則不會引起注意。

通常,人們在解決這樣的:

def test(a=None): 
    if a is None: 
     a = {} 
    # ... etc. 
+0

雅很好的解釋。我想添加另一行。函數是python中的一個對象,當我們調用第一次創建函數對象時,之後我們重用了同一個對象。您可以在每次通話後通過打印ID(測試)進行確認。你將得到相同的函數對象id,這意味着只有一個對象正在共享。 – 2013-11-27 02:12:39

3

通常,默認方法參數不應該是可變的。相反,這樣做:

def test(self, out=None): 
    out = out or {} 
    # other code goes here. 

見這些鏈接的更多細節,爲什麼這是必要的和爲什麼它是一個Python語言,而不是一個錯誤的「功能」。

+3

out = out或{}'不僅有一個完美的味道,而且如果調用者傳入自己的空映射目的。 '如果out是None:out = {}'是可取的。 – 2010-10-08 02:00:08

2

你在你的方法修改函數關鍵字參數out的價值。

This blog post解釋它succintly:當功能被定義,當它被稱爲在默認參數

表達式計算。

該函數是在創建類時定義的,而不是針對每個實例定義的。如果你修改它像這樣,問題消失:

def test(self,out=None): 
     if out is None: 
       out = {} 
     key = "key" 
     for i in ['a','b','c','d']: 
       if key in out: 
         out[key] += ','+i 
       else: 
         out[key] = i 
     return out