使用自动递增实例属性实现类的最Pythonic方法是什么?

时间:2013-06-18 15:40:11

标签: c++ python inheritance composition

我有几节课。实例创建的所需行为是为实例分配ID。为简单起见,我们假设ID应从0开始,并在每次创建实例时增加1。对于这几个类中的每一个,ID应该独立递增。

我知道如何在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; 
}

到作曲方式:

#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 ++中,我很清楚我在做什么,例如即使我的类实际上继承了multiply(不仅仅是来自Countable&lt;&gt;模板化类),我也没有看到任何问题 - 一切都很简单。

现在,在Python中,我看到了以下问题:

1)当我使用合成时,我实例化计数类:

counterClass = Countable()

我必须为每个班级做这个,这可能容易出错。

2)当我使用继承时,当我想要加倍时,我会遇到进一步的麻烦。注意上面,我没有定义Counted的__init__和AnotherCounted,但是如果我继承了multiply,我必须显式地调用基类构造函数,或者使用super()。我不喜欢这个(但是?)我也可以使用元类,但我的知识有限,似乎它增加了复杂性而不是简单性。

总之,我认为组合方式对于Python实现可能更好,尽管必须使用Countable()显式定义counterClass类属性。

我很感激您对我的结论的有效性的看法。

我也很欣赏有关比我更好的解决方案的提示。

谢谢。

2 个答案:

答案 0 :(得分: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

答案 1 :(得分: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

如果你真的想这样做,那就是&#34;幻想&#34;方式,你可以使用元类:

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