2012-08-09 79 views
0

可能重複:
「Least Astonishment」 in Python: The Mutable Default Argument瞭解Python的繼承和初始化

在Python 2.7,考慮我有以下代碼:

class Base(object): 
    # Variant 1 
    def __init__(self, records=[]): 
     self._records = records 

    # Variant 2 
    # def __init__(self, records=[]): 
    #  self._records = [] 
    #  if records: 
    #   self._records = records 

    def append(self, value): 
     self._records.append(value) 

class ChildA(Base): 
    pass 

class ChildB(Base): 
    pass 

a = ChildA() 
b = ChildB() 
a.append(100) 
b.append(200) 

print a._records 
print b._records 

如果我使用的變體1初始化我的基類self._records的行爲就像一個類變量。執行使用變體1初始化我的基類中的代碼,我得到的輸出中:

[100, 200] 
[100, 200] 

使用變種2初始化我的基類,self._records就像一個實例變量(如預期)。執行使用變種2初始化我的基類中的代碼,我得到的輸出:

[100] 
[200] 

的是這兩種變體之間的區別?爲什麼變體1的工作方式與變體2不同?非常感謝你的幫助!

回答

1

您的默認參數是[],這是Python常見的錯誤。 tutorial

重要警告:默認值只計算一次。當默認值是可變對象(如 列表,字典或大多數類的實例)時,此 會有所不同。

1

它與繼承,類或實例變量無關。考慮下面的代碼:

>>> def f(a=[]): 
...  a.append(1) 
...  print a 
... 
>>> f.func_defaults 
([],) 
>>> f() 
[1] 
>>> f() 
[1, 1] 
>>> f.func_defaults 
([1, 1],) 

函數參數的默認值只被計算一個並存儲在函數對象中。每次調用f時,它都使用相同的列表進行操作。正如你的情況一樣。

0

正如其他人所說的 - 這與使用空列表作爲默認值有關,而不是類繼承行爲。

做正確的方法是:

class Base(object): 
    # Variant 1 
    def __init__(self, records=None): 
     if records is None: 
      records = [] 
     self._records = records 

因此確保一個新的列表實例每一個基類實例化時創建。將它放入代碼中的方式是,在用Python解析類體時實例化一個列表對象 - 每次運行__init__方法時,將使用list的一個實例作爲默認參數。由於對象擁有引用並更改同一列表,所以更改在共享Base類的所有其他對象中都可見。