2011-10-26 63 views
4

我有一個Python類,有一個類屬性設置爲None以外的東西。創建新實例時,對該屬性所做的更改會在所有實例中保留。具有非無類屬性的類的新實例?

下面是一些代碼來理解這一點:

class Foo(object): 
    a = [] 
    b = 2 

foo = Foo() 
foo.a.append('item') 
foo.b = 5 

使用foo.a回報['item']foo.b回報5,正如人們所期望的那樣。

當我創建一個新的實例(我們稱它爲bar),使用bar.a回報['item']bar.b回報5,太!然而,當我最初設定所有的類屬性None然後將它們設置爲任何在__init__,像這樣:

class Foo(object): 
    a = None 
    b = None 

    def __init__(self): 
    self.a = [] 
    self.b = 2 

使用bar.a回報[]bar.b回報2foo.a回報['item']foo.b回報5

這是怎麼回事假設工作?在我編寫Python的3年中,我顯然從未遇到過這個問題,並希望得到一些解釋。我也無法在文檔中的任何地方找到它,所以給我一個參考將是非常好的,如果可能的話。 :)

+1

你是否確定*'bar.b'是5? – SingleNegationElimination

回答

7

是的,這是它應該如何工作。

如果ab屬於實例Foo,然後做到這一點,正確的做法是:

class Foo(object): 
    def __init__(self): 
    self.a = [] 
    self.b = 2 

下使ab屬於類本身,所以所有實例共享相同的變量:

class Foo(object): 
    a = [] 
    b = 2 

當你混合兩種方法 - - 就像你在第二個例子中所做的那樣 - 這不會增加任何有用的東西,只會造成混淆。

一個忠告值得一提的是,當你做你的第一個例子如下:

foo.b = 5 

沒有更改Foo.b,要添加一個全新的屬性foo說,「陰影」 Foo.b。當你這樣做時,bar.bFoo.b都不會改變。如果您隨後執行,則會刪除該屬性,foo.b將再次參考Foo.b

+0

啊,我明白了!直到現在,我從未意識到類和實例屬性之間的區別!我總是認爲類屬性是在類本身中定義的(例如示例),並且會使用'foo.c = 2'(一個即時定義,如果您願意的話)來調用一個實例。說得通!謝謝! –

0

您將類屬性與實例屬性混淆。在第一個示例中,只有類屬性ab,但在第二個示例中,您也有具有相同名稱的實例屬性。

看來,如果沒有實例屬性(我今天學到了一些新東西!),Python將通過從實例到類的屬性的引用。

你會發現這個代碼啓發:

class Foo: 
    a = 1 
    b = 2 

    def __init__(self): 
    self.a=3 

print Foo.a # => 1 
print Foo().a # => 3 
print Foo.b # => 2 
print Foo().b # => 2 
+0

謝謝!我一定把這兩個混淆了! –

1

是的,這就是你應該期望什麼。當您在類上定義一個變量時,那些屬性將附加到類,而不是實例。您可能並不總是注意到,當您將分配給到實例上的某個屬性時,該實例將拾取新值,從而屏蔽該類屬性。方法就位,如list.append將不會給該實例一個新的屬性,因爲他們只是修改現有的對象,這恰好是該類的屬性。

任何時候,每個類的每個實例都應該有一個屬性的唯一值,您通常應該在__init__方法中設置它,以確保它對每個實例都不相同。

只有當某個類的某個屬性具有合理的默認值,並且該值屬於不可更改的類型(不能在適當位置修改)時(例如intstr),您應該在該類上設置屬性。

+0

謝謝!就像我在上面的回答中所說的那樣,我顯然不知道類和實例屬性之間的區別!我想這可能是自學成才和不耐煩的一個警告。 ;) –

1

是的,它似乎令人驚訝的開始,但它是蟒蛇的工作方式,如其他答案中所述。三年的蟒蛇沒有被削減?你很幸運,很幸運。如果我是你,我會檢查敏感的過去的代碼爲小動物...

還要小心函數調用:def func(list=[])有類似的行爲,這可能會令人驚訝。

並且,如果可能的話,在你的提交鉤子中插入一個pylint解析。或者至少在你的代碼上運行pylint,這是非常有啓發性的,並且可能會告訴你關於這個技巧(這不是一個技巧,事實上,它是非常邏輯和正交的,只是python的方式)。

+0

剛開始使用pylint。這非常有幫助!謝謝! –