標題名字有點長。
性子急的可以直接跳到最後看總結。
原帖子位址
為了方便對比,我還是使用原帖子的例子:12
3456
78910
1112
1314
15classaaa():
aaa=10
# 情形1
obj1=aaa()
obj2=aaa()
print obj1.aaa,obj2.aaa,aaa.aaa
# 情形2
obj1.aaa+=2
print obj1.aaa,obj2.aaa,aaa.aaa
# 情形3
aaa.aaa+=3
print obj1.aaa,obj2.aaa,aaa.aaa
情形1的結果是:10 10 10
;
情形2的結果是:12 10 10
;
情形3的結果是:12 13 13
;
首先為什麼會有這個問題呢?
因為aaa
屬性被稱為類屬性,既然是類屬性,那麼根據從c++/j**a
這種靜態語言使用的經驗來判斷,類屬性應該是為其實例所共享的。很自然的,既然是共享關係,那麼從類的層次改變aaa
的值,自然其實例的aaa
的值也要跟著變化了。
可是情形3的情況卻說明,上面的說法是錯的。
錯**呢?
要從python的類屬性講起
python屬於動態強型別的語言,在很多地方和靜態語言不同,因此,不能把靜態語言的規則套到動態語言上來。其中,類屬性就是乙個很好的例子。
python中屬性的獲取
對於屬性,我們通常採用類.屬性或例項.屬性的形式呼叫。
例如上例中的aaa.aaa
屬於類.屬性形式,obj1.aaa
屬於例項.屬性的形式
python中屬性的設定
對於屬性的設定我們通常採用類.屬性 = 值或例項.屬性 = 值的形式
例如obj1.aaa = 3
上例中obj1.aaa += 2
等價於obj1.aaa = obj1.aaa + 2
,這句話包含了屬性獲取及屬性設定兩個操作
ok,重點來了,python中屬性的獲取和設定的機制與靜態語言是不同的,正是背後機制的不同,導致了python中類屬性不一定是為其實例所共享的
python中屬性的獲取存在乙個向上查詢機制,還是拿上面的例子做說明:
python中一切皆物件,aaa
屬於類物件,obj1
屬於例項物件,從物件的角度來看,aaa
與obj1
是兩個無關的物件,但是,python通過下面的查詢樹建立了類物件aaa
與例項物件obj1
、obj2
之間的關係。
如圖所示12
345aaa
|-----
||obj1 obj2
(圖畫的不好,見諒 -.-!!!)
當呼叫aaa.aaa
時,直接從aaa
獲取其屬性aaa
。
但是情形1中呼叫obj1.aaa
時,python按照從obj1
到aaa
的順序由下到上查詢屬性aaa
。
值得注意的這時候obj1
是沒有屬性aaa
的,於是,python到類aaa
中去查詢,成功找到,並顯示出來。所以,從現象上來看,aaa
的屬性aaa
確實是共享給其所有例項的,雖然這裡只是從查詢樹的形式模擬了其關係。
原帖子的作者也指出問題的關鍵在於情形2中obj1.aaa += 2
。
為什麼呢?
上面我們指出obj.aaa += 2
包含了屬性獲取及屬性設定兩個操作。即obj1.aaa += 2
等價於obj1.aaa = obj1.aaa + 2
。
其中等式右側的obj.aaa
屬於屬性獲取,其規則是按照上面提到的查詢規則進行,即,這時候,獲取到的是aaa
的屬性aaa
,所以等式左側的值為12
。
第二個操作是屬性設定,即obj.aaa = 12
。當發生屬性設定的時候,obj1
這個例項物件沒有屬性aaa
,因此會為自身動態新增乙個屬性aaa
。
由於從物件的角度,類物件和例項物件屬於兩個獨立的物件,所以,這個aaa
屬性只屬於obj1
,也就是說,這時候類物件aaa
和例項物件aaa
各自有乙個屬性aaa
。
那麼,在情形3中,再次呼叫obj1.aaa
時,按照屬性呼叫查詢規則,這個時候獲取到的是例項物件obj1
的屬性aaa
,而不是類物件aaa
的屬性aaa
。
到這裡就可以完滿解釋上面的問題:
1. python中屬性的獲取是按照從下到上的順序來查詢屬性;
2. python中的類和例項是兩個完全獨立的物件;
3. python中的屬性設定是針對物件本身進行的;
對情形1的解釋
因為python中的屬性獲取是按照從下到上的順序來查詢的,所以在情形1:12
obj1=aaa()
obj2=aaa()
例項物件obj1
和obj2
不存在屬性aaa
。
證明如下:12
34>>>obj1.__dict__
{}>>>obj2.__dict__
{}所以,此時,obj1.aaa, obj2.aaa, aaa.aaa
實質上都是指aaa.aaa
。因此,輸出同樣的結果。
對情形2的解釋
因為python中的類和例項是兩個完全獨立的物件且python中的屬性設定是針對物件本身進行的,所以在情形2:
1obj1.aaa+=2
實質上是對例項物件obj1
設定了屬性aaa
,並賦值為12
。證明如下:12
345>>>obj1.aaa=3
>>>obj1.__dict__
>>>obj2.__dict__
{}因此,再次呼叫obj1.aaa
時,將獲取到的是例項物件obj1
的屬性aaa
,而不是類物件aaa
的屬性aaa
。而對於例項物件obj2
,由於其並沒有屬性aaa
,所以呼叫obj2.aaa
時,獲取到的是aaa
的屬性aaa
。
對情形3的解釋
順利理解了前兩個情形,那麼第3個情形就很容易了,改變aaa
的屬性aaa
只能影響到類物件aaa
和例項物件obj2
,不能影響obj1
,因為,obj1
存在aaa
,在獲取時,不會獲取到aaa
的屬性。
問題本身很簡單,但是通過對這個問題的**,可以深入理解python作為乙個動態語言,在oop的機制上與靜態語言的差別。
最關鍵的地方在於兩點:
1. 理解python是如何利用查詢樹的機制來模仿類及例項之間的關係;
2. 理解動態語言是可以動態設定屬性的
Python 類屬性與例項屬性
python在 init self 方法中定義的屬性為例項屬性,並且python的例項可以動態地增加例項屬性 直接跟在類名後邊定義的屬性都預設是類屬性 類似於c 的static變數 要明白雷和例項都是命名空間。類是類屬性的命名空間,例項則是例項屬性的命名空間。例項屬性能夠在名稱訪問 搜尋 中覆蓋類屬...
Python例項屬性與類屬性
class foo object version 1.0 f foo print foo.version 1.0 print f.version 1.0 foo.version 0.1 print foo.version 1.1 print f.version 1.1 f.version 0.1 p...
Python類屬性與例項屬性
類屬性為所有例項擁有,例項屬性為單個例項擁有class cls object count 0 count 0def init self cls.count cls.count 1 self.count self.count 1 p cls print 類屬性 d p.count print 例項屬性...