2013-03-11 83 views
2

我發現奇怪的Python行爲(或者我不明白屬性的繼承和/或默認值是如何工作的)。Python:變量的新實例未在類的新實例中創建

對於給定的代碼

class A(object): 
    def __init__(self, s): 
     self.s = s 
     print "in init", self.s 

class B(A): 
    def __init__(self, s = set()): 
     super(B, self).__init__(s) 
     print "after super", self.s 
     self.s.add('foo') 
     print '--------------' 

if __name__ == "__main__": 
    a = B() 
    b = B() 

我得到以下輸出:

in init set([]) 
after super set([]) 
-------------- 
in init set(['foo']) # Why it has value set in other object?! 
after super set(['foo']) 
-------------- 

當然期望的行爲將是一個空集,以初始化self.s在第二物體(b)中,但對於它從以前的對象獲取狀態的未知原因。爲什麼會發生?如何獲得所需的行爲?

謝謝!

+2

http://stackoverflow.com/questions/1132941/least-astonishment-in-python-the-mutable-default-argument – 2013-03-11 16:20:32

+1

非常感謝您!這真的很有說服力! – radious 2013-03-11 16:28:50

回答

7

你已經陷入了可變的默認參數陷阱。您在B的__init__中指定爲默認值的set()與將傳遞到您創建的每個新B的對象相同。將它放入self不會生成新副本,它只會創建對同一對象的引用。

你可能想在每個新對象的副本,這樣做明確:

class A(object): 
    def __init__(self, s): 
     self.s = copy.copy(s) 
+0

非常感謝! – radious 2013-03-11 16:29:26

+0

在這個特殊情況下使用'copy()',儘管最常見的Python模式是使用'None'作爲默認值,我給出了一個例子。 – 2013-03-11 16:36:21

+0

@MattGood,我根據一種直覺感到,即使給出明確的論點時副本是最合適的,我也提出了我的建議。對象持有並修改對其自身之外的引用是很平常的。 – 2013-03-11 16:45:23

0

當您設置一個默認的參數,數值在時間計算你定義功能,而不是當你打電話時。因此,它使用的是在定義該類時創建的相同的set(),並向其中添加了越來越多的項目。

相反,典型的蟒紋是用None爲默認值,然後明確地初始化新set()(或列表,字典,或任何對象,你想要的)的__init__體內。

class B(A): 
    def __init__(self, s=None): 
    if s is None: 
     s = set()