16

Below, base_id and _id is a class variable and shared among all child classes.
Is there a way to separate them into each class?

from itertools import count

class Parent(object):
    base_id = 0
    _id = count(0)

    def __init__(self):
        self.id = self.base_id + self._id.next()


class Child1(Parent):
    base_id = 100
    def __init__(self):
        Parent.__init__(self)
        print 'Child1:', self.id

class Child2(Parent):
    base_id = 200
    def __init__(self):
        Parent.__init__(self)
        print 'Child2:', self.id

c1 = Child1()                   # 100
c2 = Child2()                   # 201 <- want this to be 200
c1 = Child1()                   # 102 <- want this to be 101
c2 = Child2()                   # 203 <- want this to be 201
falsetru
  • 357,413
  • 63
  • 732
  • 636
eugene
  • 39,839
  • 68
  • 255
  • 489
  • Is there any particular reason why you want the hundreds digit to represent the class? What about if you get more than 100 instances? Then you'll have overlap. Where will you see the ID, and could there be a better way to distinguish between classes than using the ID? (Perhaps you could display some extra information, for instance.) – jpmc26 Nov 01 '13 at 02:17
  • 3
    That doesn't actually matter here; even if eugene changed IDs to 2-tuples (e.g. (1,0), (1,1), (1,2)... for `Child1`) the problem of creating independent counters remains. – Mike DeSimone Nov 01 '13 at 02:19

3 Answers3

10

If you really need to use the ID this way, use parameters:

class Parent(object):
    def __init__(self, id):
        self.id = id

class Child1(Parent):
    _id_counter = count(0)
    def __init__(self):
        Parent.__init__(self, 100 + self._id_counter.next())
        print 'Child1:', self.id

etc.

This assumes you won't be constructing instances of Parent directly, but that looks reasonable with your example code.

jpmc26
  • 28,463
  • 14
  • 94
  • 146
4

As you said in the question, _id is shared by parent and all children classes. Define _id for every children classes.

from itertools import count

class Parent(object):
    base_id = 0
    _id = count(0)

    def __init__(self):
        self.id = self.base_id + self._id.next()


class Child1(Parent):
    base_id = 100
    _id = count(0) # <-------
    def __init__(self):
        Parent.__init__(self)
        print 'Child1:', self.id

class Child2(Parent):
    base_id = 200
    _id = count(0) # <-------
    def __init__(self):
        Parent.__init__(self)
        print 'Child2:', self.id

c1 = Child1()                   # 100
c2 = Child2()                   # 200
c1 = Child1()                   # 101
c2 = Child2()                   # 201

UPDATE

Using metaclass:

class IdGenerator(type):
    def __new__(mcs, name, bases, attrs):
        attrs['_id'] = count(0)
        return type.__new__(mcs, name, bases, attrs)

class Parent(object):
    __metaclass__ = IdGenerator
    base_id = 0
    def __init__(self):
        self.id = self.base_id + next(self._id)
falsetru
  • 357,413
  • 63
  • 732
  • 636
4

If you don't want to violate the DRY principle like falsetru suggests, you'll need to use metaclasses. I was thinking of writing something up, but there's already a good long description of metaclasses on SO, so check it out.

Metaclasses, in short, let you control subclass creation.

Basically, what you need to do is, upon the creation of a subclass of Parent, add the _id member to the newly-created subclass.

Community
  • 1
  • 1
Mike DeSimone
  • 41,631
  • 10
  • 72
  • 96