2011-11-09 39 views
27

我有一個關於Python的__class__的問題。Python中的屬性「__class__」是什麼

該文檔說__class__是類實例所屬的類。於是我進行了一系列實驗:

class counter: 
    count = 0 
    def __init__(self): 
      self.__class__.count += 1 

NewCounter1 = counter() 
print NewCounter1.count #The result is 1 
NewCounter2 = counter() 
print NewCounter2.count #The result is 2 
print NewCounter2.__class__.count is NewCounter2.count #result: True 

一切順利。

然後我輸入的代碼如下:

NewCounter2.__class__.count = 3 

print NewCounter1.count     #result:3 
print NewCounter1.__class__.count  #result:3 
print NewCounter2.count     #result:3 
print NewCounter2.__class__.count  #result:3 
print NewCounter2.__class__.count is NewCounter2.count  #result: True 

從上面的代碼,我想,也許NewCounter1.count等於NewCounter1,或__class__.count,但下面的代碼讓我大吃一驚:

NewCounter2.count = 5 

print NewCounter1.count     #result:3 
print NewCounter1.__class__.count #result:3 
print NewCounter2.count     #result:5 
print NewCounter2.__class__.count #result:3 
print NewCounter2.__class__.count is NewCounter2.count  #result: False 

爲什麼NewCounter2.count更改,但NewCounter2.__class__.count保持在3?更重要的是,當我更換NewCounter2.count時,NewCounter2.__class__.count is NewCounter2.count變成了False。什麼是屬性__class__

+8

對於冗長的問題+1。 –

+2

另外,'是'不會做你認爲它在這裏做的事情,特別是關於小整數。 –

回答

19

「從上面的代碼,我想,也許NewCounter1.count等於NewCounter1 _class_.count。」

的問題是,這句話在你的問題的那一刻,唯一的指令後:

NewCounter1 = counter() 
NewCounter2 = counter() 
NewCounter2.__class__.count = 3 

具有創建NewCounter1NewCounter2
和具有MODI田間類屬性counter.count
有存在沒有對象NewCounter1.count也不NewCounter2.count,然後「等於」沒有實際意義。

NewCounter1,只是以後的創作:

class counter: 
    count = 0 
    def __init__(self): 
     self.__class__.count += 1 

print 'counter.count BEFORE ==',counter.count # The result is 0 
NewCounter1 = counter() 
print '\nNewCounter1.__dict__ ==',NewCounter1.__dict__ # The result is {} 
print 'NewCounter1.count ==',NewCounter1.count # The result is 1 
print 'counter.count AFTER ==',counter.count # The result is 1 

NewCounter._ 字典 _是實例的名稱空間NewCounter1
print NewCounter1.count版畫一樣print counter.count
但是,'count'(字符串'count')不在名稱空間NewCounter1,也就是說在創建的實例的名稱空間中沒有屬性count

這怎麼可能?

這是因爲實例不assignement創建一個「計數」標識內_ 初始化 _
- >存在NewCounter1沒有真正的創造任何屬性作爲一個字段,即說不創建INSTANCE屬性。

的結果是,當指令
print 'NewCounter1.count ==',NewCounter1.count
評估,解釋沒有找到在NewCounter1的命名空間的實例屬性,然後進入該類的實例來搜索在這個類的名字空間中鍵入'count';在那裏它發現「count」作爲CLASS屬性的關鍵字,並且可以將對象counter.count的VALUE作爲VALUE顯示以響應該指令。

一個類實例有一個實現爲字典的名稱空間,它是 第一個在其中搜索屬性引用的地方。當在那裏找不到 屬性,並且該實例的類具有該名稱的 屬性時,搜索將繼續使用類 屬性。 http://docs.python.org/reference/datamodel.html#the-standard-type-hierarchy

所以,NewCounter1.count equals NewCounter1.__class__.count這裏意味着NewCounter1.count的價值,即使這一個實際上並不存在,是類屬性NewCounter1的值。 .count。這裏的「被」是英文動詞,而不是功能的是,測試兩個對象的身份的語言,這意味着「被認爲具有」

當執行NewCounter2.__class__.count = 3,只有類屬性計數器.count受影響。 NewCounter1NewCounter2的命名空間保持爲空,並且類似地找到counter.count的值。

在結束時,當執行NewCounter2.count = 5,此時實例屬性計數作爲在NewCounter2對象的字段創建和「計數」出現在NewCounter2的命名空間。
它沒有覆蓋任何東西,因爲沒有任何的實例的__dict__
沒有其他變化之前影響NewCounter1counter.count

下面的代碼執行過程中顯示更明確的基本事件:

from itertools import islice 

class counter: 
    count = 0 
    def __init__(self): 
     print (' | counter.count first == %d at %d\n' 
       ' |  self.count first == %d at %d')\ 
       % (counter.count,id(counter.count), 
        self.count,id(self.count)) 

     self.__class__.count += 1 # <<===== 

     print (' | counter.count second == %d at %d\n' 
       ' |  self.count second == %d at %d\n' 
       ' | id(counter) == %d id(self) == %d')\ 
       % (counter.count,id(counter.count), 
        self.count,id(self.count), 
        id(counter),id(self)) 



def display(*li): 
    it = iter(li) 
    for ch in it: 
     nn = (len(ch)-len(ch.lstrip('\n')))*'\n' 
     x = it.next() 
     print '%s == %s %s' % (ch,x,'' if '__dict__' in ch else 'at '+str(id(x))) 



display('counter.count AT START',counter.count) 


print ('\n\n----- C1 = counter() ------------------------') 
C1 = counter() 
display('C1.__dict__',C1.__dict__, 
     'C1.count ',C1.count, 
     '\ncounter.count ',counter.count) 


print ('\n\n----- C2 = counter() ------------------------') 
C2 = counter() 
print (' -------------------------------------------') 
display('C1.__dict__',C1.__dict__, 
     'C2.__dict__',C2.__dict__, 
     'C1.count ',C1.count, 
     'C2.count ',C2.count, 
     'C1.__class__.count',C1.__class__.count, 
     'C2.__class__.count',C2.__class__.count, 
     '\ncounter.count ',counter.count) 


print '\n\n------- C2.__class__.count = 3 ------------------------\n' 
C2.__class__.count = 3 
display('C1.__dict__',C1.__dict__, 
     'C2.__dict__',C2.__dict__, 
     'C1.count ',C1.count, 
     'C2.count ',C2.count, 
     'C1.__class__.count',C1.__class__.count, 
     'C2.__class__.count',C2.__class__.count, 
     '\ncounter.count ',counter.count) 


print '\n\n------- C2.count = 5 ------------------------\n' 
C2.count = 5 
display('C1.__dict__',C1.__dict__, 
     'C2.__dict__',C2.__dict__, 
     'C1.count ',C1.count, 
     'C2.count ',C2.count, 
     'C1.__class__.count',C1.__class__.count, 
     'C2.__class__.count',C2.__class__.count, 
     '\ncounter.count ',counter.count) 

結果

counter.count AT START == 0 at 10021628 


----- C1 = counter() ------------------------ 
    | counter.count first == 0 at 10021628 
    |  self.count first == 0 at 10021628 
    | counter.count second == 1 at 10021616 
    |  self.count second == 1 at 10021616 
    | id(counter) == 11211248 id(self) == 18735712 
C1.__dict__ == {} 
C1.count == 1 at 10021616 

counter.count == 1 at 10021616 


----- C2 = counter() ------------------------ 
    | counter.count first == 1 at 10021616 
    |  self.count first == 1 at 10021616 
    | counter.count second == 2 at 10021604 
    |  self.count second == 2 at 10021604 
    | id(counter) == 11211248 id(self) == 18736032 
    ------------------------------------------- 
C1.__dict__ == {} 
C2.__dict__ == {} 
C1.count == 2 at 10021604 
C2.count == 2 at 10021604 
C1.__class__.count == 2 at 10021604 
C2.__class__.count == 2 at 10021604 

counter.count == 2 at 10021604 


------- C2.__class__.count = 3 ------------------------ 

C1.__dict__ == {} 
C2.__dict__ == {} 
C1.count == 3 at 10021592 
C2.count == 3 at 10021592 
C1.__class__.count == 3 at 10021592 
C2.__class__.count == 3 at 10021592 

counter.count == 3 at 10021592 


------- C2.count = 5 ------------------------ 

C1.__dict__ == {} 
C2.__dict__ == {'count': 5} 
C1.count == 3 at 10021592 
C2.count == 5 at 10021568 
C1.__class__.count == 3 at 10021592 
C2.__class__.count == 3 at 10021592 

counter.count == 3 at 10021592 

一個有趣的事情是增加一個指令
self.count = counter.count
前行
self.__class__.count += 1 # <<=====
觀察結果

的變化。

總之,這個問題並不是關於__class__,而是關於搜索一個屬性的機制,而這種機制在被忽略時會產生誤導。

+2

非常感謝您的耐心和熱忱。我不知道該說什麼,但謝謝。我真的瞭解這個機制。你說的很詳細。再次感謝! – Searene

+2

謝謝。我喜歡詳細地研究一些事情,並且我喜歡寫一些內容儘可能清晰,含糊不清,措詞不清的文字。第一個提高了理解能力,第二個允許測試這個理解是否穩定。我希望你在SO上玩得開心。 – eyquem

+0

@eyquem非常感謝。這個答案是如此豐富的信息,我必須至少再讀一遍才能真正得到它。我希望所有的答案都是如此高質量。 – Inversus

3

重新綁定(即分配)具有與該類上的屬性相同名稱的對象的屬性會遮蔽該類上的屬性。首先檢查對象的屬性,然後是MRO順序的類。

10

這條線:

NewCounter2.__class__.count = 3 

改變的counter靜態count,但在這裏:

NewCounter2.count = 5 

NewCounter2現在有自己的count屬性隱藏靜態count;
以便該線對NewCounter1沒有影響。
這也是爲什麼NewCounter2.__class__.count != NewCounter2.count

+4

混淆似乎會出現,因爲雖然您可以在'counter'實例上_get_屬性'counter.count',您不能直接在'counter'實例上設置屬性'counter.count'。 – agf

0

NewCounter2.count = 5 

創建新實例級別的NewCounter2屬性。之後,您訪問兩個不同的屬性(NewCounter2.count - 實例級別attr和NewCounter2.__class__.count - 類級別attr),這是導致「奇怪」行爲的原因。