2013-06-18 18 views
2

我有幾個班。創建實例所需的行爲是爲實例分配一個ID。爲了簡單起見,讓我們假設ID應該從0開始,並在創建每個實例時增加1。對於這幾個類中的每一個,都應該獨立地增加ID。使用自動遞增實例屬性實現類的最Python方式是什麼?

我知道如何在C++中做到這一點。我實際上也是用Python做的,但我不喜歡C++解決方案,我想知道是否是由於我對Python的瞭解有限(超過6周),或者是否有更好的方法,更多的Pythonic方式。

在C++中,我已經實現了這兩個使用繼承,並使用組合。兩種實現都使用「好奇循環模板模式」(CRPT)慣用語。我微微喜歡的繼承方式:

#include <iostream> 

template<class T> 
class Countable{ 
    static int counter; 
public: 
    int id; 
    Countable() : id(counter++){} 
}; 
template<class T> 
int Countable<T>::counter = 0; 

class Counted : public Countable<Counted>{}; 
class AnotherCounted: public Countable<AnotherCounted>{}; 

int main(){ 
    Counted element0; 
    Counted element1; 
    Counted element2; 
    AnotherCounted another_element0; 
    std::cout << "This should be 2, and actually is: " << element2.id << std::endl; 
    std::cout << "This should be 0, and actually is: " << another_element0.id << std::endl; 
} 

到composion方式:現在

#include <iostream> 

template<class T> 
class Countable{ 
    static int counter; 
public: 
    int id; 
    Countable() : id(counter++){} 
}; 
template<class T> 
int Countable<T>::counter = 0; 

class Counted{ 
public: 
    Countable<Counted> counterObject; 
}; 

class AnotherCounted{ 
public: 
    Countable<AnotherCounted> counterObject; 
}; 


int main(){ 
    Counted element0; 
    Counted element1; 
    Counted element2; 
    AnotherCounted another_element0; 
    std::cout << "This should be 2, and actually is: " << element2.counterObject.id << std::endl; 
    std::cout << "This should be 0, and actually is: " << another_element0.counterObject.id << std::endl; 
} 

,在Python中,沒有模板,這將使我爲每一類不同的計數器。因此,我包可數類的功能,並取得了以下實現:(繼承的方式)

def Countable(): 
    class _Countable: 
     counter = 0 
     def __init__(self): 
      self.id = _Countable.counter 
      _Countable.counter += 1 

    return _Countable 


class Counted (Countable()) : 
    pass 

class AnotherCounted(Countable()): 
    pass 

element0 = Counted() 
element1 = Counted() 
element2 = Counted() 
another_element0 = AnotherCounted() 

print "This should be 2, and actually is:", element2.id 
print "This should be 0, and actually is:", another_element0.id 

和組成方式:

def Countable(): 
    class _Countable: 
     counter = 0 
     def __init__(self): 
      self.id = _Countable.counter 
      _Countable.counter += 1 

    return _Countable 


class Counted (Countable()) : 
    counterClass = Countable() 
    def __init__(self): 
     self.counterObject = Counted.counterClass() 

class AnotherCounted(Countable()): 
    counterClass = Countable() 
    def __init__(self): 
     self.counterObject = self.counterClass() 

element0 = Counted() 
element1 = Counted() 
element2 = Counted() 
another_element0 = AnotherCounted() 

print "This should be 2, and actually is:", element2.counterObject.id 
print "This should be 0, and actually is:", another_element0.counterObject.id 

什麼困擾我是這樣的。在C++中,我有一個好主意,我在做什麼,例如即使我的類實際上繼承了繁殖(不僅來自Countable <>模板類),我看不出任何問題 - 一切都非常簡單。

現在,在Python中,我看到了以下問題:

1)當我用的組合物,我實例計數類這樣的:

counterClass = Countable() 

我必須爲每一個類做到這一點,這可能很容易出錯。 2)當我使用繼承時,當我想要ihnerit繁殖時,我會遇到更多麻煩。請注意,上面我沒有定義Counsed或AnotherCounted的__init__,但是如果我繼承了multiply,我將不得不顯式調用基類構造函數,或者使用super()。我不喜歡這個(但?)我也可以使用元類,但是我的知識僅限於此,它似乎增加了複雜性而不是簡單性。

總而言之,儘管必須使用Countable()明確定義counterClass類屬性,但我認爲組合方式對於Python實現可能更好。

我希望你對我的結論的有效性發表意見。

我也很欣賞關於比我更好的解決方案的提示。

謝謝。

回答

4

我會用__new__,這樣你就不必記住在__init__做任何事情:

class Countable(object): 
    counter = 0 
    def __new__(cls, *a, **kw): 
     instance = super(Countable, cls).__new__(cls, *a, **kw) 
     instance.id = cls.counter + 1 
     cls.counter = instance.id 
     return instance 


class A(Countable): 
    pass 


class B(Countable): 
    pass 


print A().id, A().id # 1 2 
print B().id   # 1 
+0

美麗的解決方案。感謝您的分享。我學到了一些新東西和有用的東西。 – ondrejdee

+0

想一想,這可以工作,因爲'計數器'(它是一個int)的不變性。順便說一句,似乎可以不使用'__new__'而是使用'__init __(self)'並將計數器稱爲'type(self).counter'來獲得相同的行爲。我現在想知道的是:我怎麼能解決類似的問題,但現在'counter'是一個列表,它應該由一個元素1添加,以便上面的代碼在第一個對象創建後產生[],[1]在第二個,[1,1]在第三等之後? – ondrejdee

+1

如果你使用'__init__',你必須記得在子類中調用'super().__ init__'。你真的需要這樣一個列表,是在'[1] * self.id'太慢的時候按需求創建的嗎?第一次創建之前應該列出什麼? –

2

我可能會使用一個簡單的類裝飾器...

import itertools 
def countable(cls): 
    cls.counter = itertools.count() 
    return cls 

@countable 
class Foo(object): 
    def __init__(self): 
     self.ID = next(self.__class__.counter) 

@countable 
class Bar(Foo): 
    pass 

f = Foo() 
print f.ID 
b = Bar() 
print b.ID 

如果你真的想這樣做的「花哨」的方式,你可以使用一個元類:

import itertools 
class Countable(type): 
    def __new__(cls,name,bases,dct): 
     dct['counter'] = itertools.count() 
     return super(Countable,cls).__new__(cls,name,bases,dct) 

class Foo(object): 
    __metaclass__ = Countable 
    def __init__(self): 
     self.ID = next(self.__class__.counter) 

class Bar(Foo): 
    pass 

f = Foo() 
print f.ID 
b = Bar() 
print b.ID 
+0

感謝您指出itertools.count()的存在。 – ondrejdee

相關問題