1

I need to make class, for which each instantiated object will have unique id (simply incremented counter). For now, all 3 objects(b, b1, b2) shares one A.__COUNTER variable.

class A(type):
__COUNTER = 0

def __call__(cls, *args, **kwargs):
    setattr(cls, "id", A.__COUNTER) 
    A.__COUNTER += 1

    return type.__call__(cls, *args, **kwargs)


class B():
    __metaclass__ = A

    def __init__(self):
       self.id

b = B()
b1 = B()
b2 = B()

print(b.id, b1.id, b2.id) -> (2, 2, 2)

Seems like I am digging in wrong direction

P.S. SOLVED

Sorry, guys, I did not mentioned that there can be couple classes which should share same id sequence. There are bunch of solutions, here how I solved it

class A(type):
__COUNTER = 0
def __call__(cls, *args, **kwargs):
    obj = type.__call__(cls, *args, **kwargs)
    obj.setId(A.__COUNTER)
    A.__COUNTER += 1

    return obj

 class B():
    __metaclass__ = A

    def setId(self, id):
        self.id = id 

class C():
   __metaclass__ = A

   def setId(self, id):
       self.id = id 

b = B()
b1 = B()
b2 = B()
c = C()
b3 = B()

print(b.id, b1.id, b2.id, c.id, b3.id) -> (0, 1, 2, 3, 4)
Constantine
  • 1,802
  • 3
  • 23
  • 37

3 Answers3

4

You can just use a class variable instead:

class B ():
    __lastId = 1

    def __init__ (self):
        self.id = B.__lastId
        B.__lastId += 1
>>> [B().id for _ in range(10)]
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

As described in What is a metaclass?, a meta class is the class of a class, whereas you just want to modify the actually instantiated objects of a single type. So there’s no need to go deep into the metaclass complexity level here.

Community
  • 1
  • 1
poke
  • 369,085
  • 72
  • 557
  • 602
3

Well, all objects in Python already have a unique id

>>> id("a string")
140588614961168

But if you want a separate counter, the method you use should work. More information on WHY you want this could help though.

Lennart Regebro
  • 167,292
  • 41
  • 224
  • 251
  • Yeap, I know about id(). I just need to make something similar (assign unique id for each instance of the class) – Constantine Dec 11 '14 at 23:15
1

A good solution depends on the use case. I've provided a very generic one.

IDAssigner is a factory that sticks an ID onto everything is creates. Create an instance of IDAssigner for a new pool of IDs. You call an instance of IDAssigner passing the class to instantiate as the first argument, followed by arguments for the class's __init__ method.

from itertools import count


class IDAssigner(object):
    def __init__(self):
        self._next_id = count()

    def __call__(self, klass, *args, **kwargs):
        obj = klass(*args, **kwargs)
        setattr(obj, 'id', next(self._next_id))
        return obj


class Foo(object):
    def __init__(self, value):
        self.value = value

    def __str__(self):
        return self.value


create = IDAssigner()
foo_1 = create(Foo, 'Hello')
foo_2 = create(Foo, 'World')
print(foo_1, foo_1.id)
print(foo_2, foo_2.id)
Peter Sutton
  • 1,145
  • 7
  • 20