Question

I am trying to create a counter of all instances created for a particular class. It is clear that my code does not increment self.__class__.counter past 1. What am I doing wrong?

 class Feature(object):
    counter = 0
    def __init__(self, line):
        self.id = self.__class__.counter
        self.__class__.counter = self.__class__.counter + 1
    def __repr__(self):
        return "Feature: id=%d name=%s" % (self.id,self.name,)

Update: This code actually works as intended. Smart folks left some very detailed explanations how statics are being handled in python. I voted to close my question, but hate if the answers will disappear.

Was it helpful?

Solution

Here is how I would do it (though your code works fine for me, so I suspect it's not the code you're actually running to observe the problem).

class Feature(object):
    counter = 0

    def __init__(self):
        self.id = Feature.counter
        Feature.counter += 1

You can replace Feature by type(self) (or self.__class__, same thing) if you prefer, but note that they'll behave differently in the presence of subclasses (maybe that's your problem, actually).

The rules for using class/instance variables are very simple in Python, and it helps to keep them in mind:

  1. Reading the value of an attribute with self.name will fall back to the class of self, and then to the base class hierarchy if self does not directly contain an attribute named name.
  2. Writing the value of an attribute with self.name = ... always binds name directly in self.

In particular, when writing a value it does not matter where a read to the same attribute would have gone before the assignment.

So if in your actual program you're instantiating a subclass of Feature, which I'll call Sub then this line:

self.__class__.counter = self.__class__.counter + 1

will probably not do what you expect. It will read from the Feature.counter (the first time you create an instance of Sub), but will write to Sub.counter. Thereafter the reads will go to Sub.counter (because it exists now), and so the Sub instances will get increasing ids, but they're not increasing the value of Feature.counter, so instances of other subclasses of Feature can get duplicate ids.

So I'd use Feature.counter if I was thinking of counter as a global variable living in the Feature namespace, and I wanted all the subclasses of Feature to share the same counter. I'd use type(self).counter if I expect each and every subclass of Feature to have its own independent counter (but then you'd need to do something to initialise them).

OTHER TIPS

I prefer this paradigm so much more when trying to count instances:

from itertools import count

class MyClass():
  _getId = count(0).next
  def __init__(self):
    self.id = self._getId()

To better illustrate the differences between self.__class__.attribute and ClassName.attribute:

class Feature(object):
    counter = 0

    def __init__(self):
        self.id = self.__class__.counter
        self.__class__.counter += 1

class Sub(Feature):
    def __init__(self):
        super(Sub, self).__init__()        

print [(vars(Feature()), vars(Sub())) for i in xrange(3)]
print Feature.counter, Sub.counter, id(Feature.counter) == id(Sub.counter)


class Feature(object):
    counter = 0

    def __init__(self):
        self.id = Feature.counter
        Feature.counter += 1

class Sub(Feature):
    def __init__(self):
        super(Sub, self).__init__()


print [(vars(Feature()), vars(Sub())) for i in xrange(3)]
print Feature.counter, Sub.counter, id(Feature.counter) == id(Sub.counter)

>>> [({'id': 0}, {'id': 1}), ({'id': 1}, {'id': 2}), ({'id': 2}, {'id': 3})]
3 4 False
>>> [({'id': 0}, {'id': 1}), ({'id': 2}, {'id': 3}), ({'id': 4}, {'id': 5})]
6 6 True

Why not use the class name itself, like

self.id = Feature.counter
Feature.counter += 1
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top