2011-09-25 94 views
6

我是python的新手,瞭解到類屬性就像C++中的靜態數據成員。然而,在嘗試下面的代碼後我感到困惑:python:class屬性和實例屬性

>>> class Foo: 
...  a=1 
... 
>>> f1=Foo(); 
>>> f2=Foo() 
>>> f1.a 
1 
>>> f1.a=5 
>>> f1.a 
5 
>>> f2.a 
1 

不應該f2.a也等於5嗎?

如果被定義爲一個列表,而不是一個整數,該行爲是正常的:

>>> class Foo: 
...  a=[] 
... 
>>> f1=Foo(); 
>>> f2=Foo() 
>>> f1.a 
[] 
>>> f1.a.append(5) 
>>> f1.a 
[5] 
>>> f2.a 
[5] 

我看着 Python: Difference between class and instance attributes,但它並沒有回答我的問題。

任何人都可以解釋爲什麼區別?由於

+0

可能的重複[如何避免讓Python類數據在實例間共享?](http:// stackoverflow。com/questions/1680528/how-do-i-avoid-having-python-class-data-shared-among-instances) –

回答

7

你在第二個例子中沒有做同樣的事情。在你第一個例子中,你正在分配f1.a一個新值:

f1.a = 5 

在你的第二個例子,你只是延伸的名單:

f1.a.append(5) 

這不會改變什麼f1.a指向。如果你是這樣做的:

f1.a = [5] 

你會發現這與你的第一個例子相同。

但考慮這個例子:

>>> f1=Foo() 
>>> f2=Foo() 
>>> Foo.a = 5 
>>> f1.a 
5 
>>> f2.a 
5 

在這個例子中,我們實際上改變屬性, 的價值的變化是在類的所有實例可見。當你 類型:

f1.a = 5 

你重寫屬性與實例屬性。

+2

」您正在使用實例屬性覆蓋類屬性。「我不這麼認爲。類屬性保持不變。在用戶11869的代碼中,** a **的屬性在** f1 **的namspace中由指令「f1.a = 5」創建,因爲此屬性以前不存在,因爲它是在我的答案中顯示使用** \ _ _ _ dict __ ** – eyquem

0

什麼情況如下:

當你實例化一個新的對象f1 = Foo(),它沒有任何自己的屬性。當您嘗試訪問例如f1.a,您重定向到類的Foo.a

print f1.__dict__ 
{} 

print f1.a 
1 

但是,如果設置f1.a = 5,實例獲取價值的一個新的屬性:

print f1.__dict__ 
{'a': 5} 

類定義不受此影響,就像任何其他實例一樣。

在第二個示例中,不要重新分配任何內容。有了append,您只能使用該類中定義的完全相同的列表。因此,您的實例仍然引用該列表,就像所有其他實例一樣。

+1

與我即將輸入的答案相同。我只是補充道,它與列表「起作用」的原因是因爲在那種情況下對象是[mutable](http://docs.python.org/library/stdtypes.html#typesseq-mutable)。由於所有實例都指向同一個對象,因此所有對這個對象的修改都可以看到。 –

+0

@Debilski:新實例實際上經常具有它們自己的屬性(您在回答中編寫的'f1 .__ dict__'是其中的一個,可以看到比較'id(Foo.a)'和'id(f1 .a)中')。 「 – EOL

5
>>> class Foo: 
...  a=1 
... 
>>> f1=Foo() 
>>> f2=Foo() 
>>> f1.a # no instance attribute here in f1, so look up class attribute in Foo 
1 
>>> f1.a=5 # create new instance attribute in f1 
>>> f1.a # instance attribute here. Great, let's use this. 
5 
>>> f2.a # no instance attribute in f2, look up class attribute in Foo 
1 
>>> 
>>> class Foo: 
...  a=[] 
... 
>>> f1=Foo() 
>>> f2=Foo() 
>>> f1.a   # no instance attribute - look up class attribute in Foo 
[] 
>>> f1.a.append(5) # no instance attribute, modify class attribute in-place 
>>> f1.a   # no instance attribute - look up class attribute in Foo 
[5] 
>>> f2.a   # same here 
[5] 
0

在python中分配屬性時,該屬性可能已經定義的位置並不重要,新分配始終應用於分配給的對象。當你說

>>> f1.a=5 

在這裏具有屬性的對象是實例,所以它是獲取新值的實例。

3

我的建議是:要了解這樣的情況下,做用id__dict__過測試:

class Foo: 
    a=1 

# Foo.a == 1 
# id(Foo.a) == 10021840 
# Foo.__dict__ == {'a': 1, '__module__': '__main__', '__doc__': None} 


f1 = Foo() 

# f1.a == 1 
# id(f1.a) == 10021840 
# f1.__dict__ == {} 

f2 = Foo() 

# f2.a == 1 
# id(f2.a) == 10021840 
# f2.__dict__ == {} 

f1.a = 5 

# f1.a == 5 
# id(f1.a) == 10021792 
# f1.__dict__ == {'a': 5} 

# f2.a == 1 
# id(f2.a) == 10021840 
# f2.__dict__ == {} 

這表明,只要指令f1.a = 5還沒有被執行,則實例f1沒有個人屬性a

那麼,爲什麼指令print f1.af1.a = 5產生1之前執行?
這是因爲:

一個類的實例有一個用字典是 在屬性引用首位被搜索實現的名字空間。當在那裏找不到 屬性,並且該實例的類具有該名稱的 屬性時,搜索將繼續使用類 屬性。

http://docs.python.org/reference/datamodel.html#the-standard-type-hierarchy

9

Python的類的屬性和對象的屬性被存儲在單獨的dictionaries。對於對象f1,可以分別通過f1.__class__.__dict__f1.__dict__訪問這些對象。執行print f1.__class__ is Foo將輸出True

當您引用對象的屬性時,Python首先會嘗試在對象字典中查找它。如果它沒有在那裏找到它,它會檢查類字典(等等繼承heirarchy)。

當您分配給f1.a時,您正在向對象字典添加條目f1。隨後查找f1.a將找到該條目。查找f2.a仍然會查找類屬性 - 類屬性字典中的條目。

可以導致f1.a值刪除它恢復到1

del f1.a 

這將刪除a項中的f1對象字典,並隨後查找將繼續以類字典。因此,之後,print f1.a將輸出1.

+0

我覺得這個答案更詳細 – Yuncy

+0

@Yuncy我認爲相同 – eyquem