0

I want to have each instance of some class have a unique integer identifier based on the order that I create them, starting with (say) 0. In Java, I could do this with a static class variable. I know I can emulate the same sort of behavior with Python, but what would be the most 'Pythonic' way to do this?

Thanks

mellort
  • 940
  • 1
  • 10
  • 19

3 Answers3

3

The following approach would be relatively pythonic (for my subjective judgement of pythonic - explicit, yet concise):

class CounterExample(object):

    instances_created = 0

    def __init__(self):
        CounterExample.instances_created += 1

    def __del__(self):
        """ If you want to track the current number of instances
            you can add a hook in __del__. Otherwise use
            __init__ and just count up.
        """
        CounterExample.instances_created -= 1

If you are facing a large number of classes, which need that kind of attribute, you could also consider writing a metaclass for that.

An example of a metaclass: http://www.youtube.com/watch?v=E_kZDvwofHY#t=0h56m10s.

miku
  • 181,842
  • 47
  • 306
  • 310
  • 1
    You might also want to put `self.identifier = CounterExample.instances_created` in the body of `__init__`, as mellort seems to want to have each object with a unique identifier. (That would, in turn, mean that you wouldn't want to decrement `instances_created` during deletion, as that could potentially result in objects initialized later having non-unique identifiers.) – JAB Jun 07 '10 at 13:48
  • This works fine until you try to do this with an inner class, at which point it fails with "CounterExample is not defined". Is there something else that you have to do to make it work with an inner class? – Zxaos Feb 28 '11 at 21:00
1

the-myyn's answer is good -- I think the class object is a perfectly fine place to stash the counter. However, note that as written it's not thread-safe. So wrap it in a classmethod that uses a lock:

import threading

class CounterExample(object):

    _next_id = 0
    _id_lock = threading.RLock()

    @classmethod
    def _new_id(cls):
        with cls._id_lock:
            new_id = cls._next_id
            cls._next_id += 1
        return new_id

    def __init__(self):
        self.id = self._new_id()

def test():
    def make_some(n=1000):
        for i in range(n):
            c = CounterExample()
            print "Thread %s; %s has id %i" % (threading.current_thread(), c, c.id)

    for i in range(10):
        newthread = threading.Thread(target=make_some)
        newthread.start()

test()

This runs 10 threads creating 1000 instances each. If you run it without the locking code, you're likely to end up with the last id lower than 9999, demonstrating the race condition.

slinkp
  • 3,406
  • 2
  • 21
  • 18
0

I guess a good question is when and how are they being created? If you're just creating a certain number of them at one point in time then use a range in a for loop.

class A:
    def __init__ (self, id):
        self.id = id
        //do other stuff

class_list = []
for i in xrange(10):
    class_list.append(A(i))

That would be one pythonic way. If your making them whenever you need them, then I think the only way is to keep an id static variable somewhere. Though I'm not sure how you're making them.

EDIT: Oh, also, when in doubt "import this" can always help you on to the right track for figuring out what is "pythonic" ;)

The Jug
  • 1,105
  • 3
  • 12
  • 26