2013-03-18 68 views
0

我想創建一個爲特定類創建的所有實例的計數器。 很明顯,我的代碼不會增加self.__class__.counter過去1.我做錯了什麼?Python靜態變量分辨率

class Feature(object): 
    counter = 0 
    def __init__(self, line): 
     self.id = self.__class__.counter 
     self.__class__.counter = self.__class__.counter + 1 
    def __repr__(self): 
     return "Feature: id=%d name=%s" % (self.id,self.name,) 

更新: 此代碼實際工作按預期。 聰明的人留下了一些非常詳細的解釋如何在Python中處理靜態。我投票結束了我的問題,但討厭答案會消失。

+0

您不是'counter'變量的一個實例屬性,您可以在'Feature'類的對象上訪問嗎? (我的意思是第二行中的那個,直接在類簽名之後) – 2013-03-18 21:59:43

+1

我看不到你解釋的行爲,你能顯示一個帶有錯誤輸出的可運行代碼段嗎? – 2013-03-18 22:00:08

+1

這段代碼在我嘗試時工作正常。 – Ben 2013-03-18 22:00:24

回答

5

這是我該怎麼做的(儘管你的代碼對我來說工作的很好,所以我懷疑這不是你實際運行的代碼來觀察問題)。

class Feature(object): 
    counter = 0 

    def __init__(self): 
     self.id = Feature.counter 
     Feature.counter += 1 

可以替代由type(self)Feature(或self.__class__,同樣的事情),如果你喜歡,但請注意,他們會表現不同的子類的存在(也許這是你的問題,實際上)。

使用類/實例變量的規則是在Python很簡單,它有助於記住它們:

  1. 閱讀與self.name屬性的值會回落到類的self,如果self不直接包含名爲name的屬性,則轉到基類層次結構。
  2. self.name = ...總是結合name直接在self寫一個屬性的值。

尤其是寫入一個值時,它不要緊哪裏讀相同的屬性將分配之前也水漲船高。

因此,如果您實際的程序你實例化一個子類的Feature,我會打電話給Sub那麼這行:

self.__class__.counter = self.__class__.counter + 1 

可能不會做你期望的。它會從Feature.counter(您第一次創建Sub的實例)讀取,但寫入Sub.counter。此後讀取將轉到Sub.counter(因爲它現在存在),因此Sub實例將獲得遞增的ID,但它們不會增加Feature.counter的值,因此Feature的其他子類的實例可以獲得重複的ID。

所以我會用Feature.counter如果我想的counter爲全局變量住在Feature命名空間,我想的Feature所有子類共享相同的計數器。如果我希望Feature的每個子類都有它自己的獨立計數器,那麼我會使用type(self).counter(但是您需要做一些初始化操作)。

+0

我發現我正在查看不同的日誌。代碼有效。正如你所描述的那樣,它的最初想法是將它與子類一起使用。無論如何,非常感謝你的解釋。 – bioffe 2013-03-18 22:27:11

+0

紅利問題:是否可以創建一個能夠爲其所有子類提供計數器的抽象類?二進制答案會做。 :) – bioffe 2013-03-18 22:41:46

+0

@bioffe幾乎肯定。 :)(無論你是指獨立的計數器還是共享的計數器) – Ben 2013-03-18 22:44:56

0

爲什麼不使用類名稱本身,像

self.id = Feature.counter 
Feature.counter += 1 
+0

如果你的類是層次結構的一部分,你可以更好地控制你的類變量將放置到哪個命名空間。你的硬編碼。 – bioffe 2013-03-18 22:10:53

+0

所以,可能我不明白Python的所有概念。子類中'__class__'的對象引用是否發生變化?更好:當'__class__'在超類中使用時,'__class__'是否指向子類'class-object? – 2013-03-18 22:13:08

+2

我可以重複使用相同的代碼,但對於重載的類還有一個單獨的計數器,前提是會有一個單獨的類級別計數器變量。 – bioffe 2013-03-18 22:19:40

1

爲了更好地說明差異self.__class__.attributeClassName.attribute之間:

class Feature(object): 
    counter = 0 

    def __init__(self): 
     self.id = self.__class__.counter 
     self.__class__.counter += 1 

class Sub(Feature): 
    def __init__(self): 
     super(Sub, self).__init__()   

print [(vars(Feature()), vars(Sub())) for i in xrange(3)] 
print Feature.counter, Sub.counter, id(Feature.counter) == id(Sub.counter) 


class Feature(object): 
    counter = 0 

    def __init__(self): 
     self.id = Feature.counter 
     Feature.counter += 1 

class Sub(Feature): 
    def __init__(self): 
     super(Sub, self).__init__() 


print [(vars(Feature()), vars(Sub())) for i in xrange(3)] 
print Feature.counter, Sub.counter, id(Feature.counter) == id(Sub.counter) 

>>> [({'id': 0}, {'id': 1}), ({'id': 1}, {'id': 2}), ({'id': 2}, {'id': 3})] 
3 4 False 
>>> [({'id': 0}, {'id': 1}), ({'id': 2}, {'id': 3}), ({'id': 4}, {'id': 5})] 
6 6 True 
+0

很好的演示。但是,如果這個課程得到延續會發生什麼。如果認爲'self____ class__'將自身綁定到類層次結構中的最低位置,這是否公平? – bioffe 2013-03-18 22:14:48

+0

取決於您的意思是最低。 '__class__'將始終是實際的類 - ypur層次的頂部 - 您的對象的類型。因此,如果只使用超級__class__'計數器來調用祖先的'__init__'將是另一個對象,但沒有'self .__ class__'它們將保持不變。所以通過使用'Feature.counter',你的子類會增加父類的計數器。 – 2013-03-18 22:43:35

1

我想指望情況下,當這麼多喜歡這種模式:

from itertools import count 

class MyClass(): 
    _getId = count(0).next 
    def __init__(self): 
    self.id = self._getId() 
+0

絕對喜歡它。很高興今天學到新東西。雖然需要一些時間來處理機制。 – bioffe 2013-03-18 22:22:15