I have this python code. The result is TopTest: attr1=0, attr2=1
for X which is fine but the result is SubTest: attr1=2, attr2=3
for Y which I don't quite understand.
Basically, I have a class attribute, which is a counter, and it runs in the __init__ method
. When I launch Y, the counter is set to 2 and only after are the attributes are assigned. I don't understand why it starts at 2. Shouldn't the subclass copy the superclass and the counter restart at 0?
class AttrDisplay:
def gatherAttrs(self):
attrs = []
for key in sorted(self.__dict__):
attrs.append('%s=%s' % (key, getattr(self, key)))
return ', '.join(attrs)
def __repr__(self):
return '[%s: %s]' % (self.__class__.__name__, self.gatherAttrs())
class TopTest(AttrDisplay):
count = 0
def __init__(self):
self.attr1 = TopTest.count
self.attr2 = TopTest.count+1
TopTest.count += 2
class SubTest(TopTest):
pass
X, Y = TopTest(), SubTest()
print(X)
print(Y)
Python Counter Counter is an unordered collection where elements are stored as Dict keys and their count as dict value. Counter elements count can be positive, zero or negative integers. However there is no restriction on it's keys and values.
Using the list count() method. The count() method counts the number of times an element appears in the list. In this method, we use the python count() method to count the occurrence of an element in a list. This method calculates the total occurrence of a given element of a list.
Accessing Elements in Python Counter To get the list of elements in the counter we can use the elements() method. It returns an iterator object for the values in the Counter.
You access and use explicitly TopTest.count
, and your subclass will stick to this explicitness. You might want to consider to use type(self).count
instead, then each instance will use its own class's variable which can be made a different one in each subclass.
To make your subclass have its own class variable, just add a count = 0
to its definition:
class SubTest(TopTest):
count = 0
You're close - when you look up a property of an object, you're not necessarily looking up a property belonging to the object itself. Rather, lookups follow Python's method resolution order, which... isn't entirely simple. In this case, however, only three steps are performed:
Y
has a property named count
.SubTest
has a property named count
.TopTest
has a property named count
. It does, so access that.Simply put, when you access Y.count
, you're actually accessing TopTest.count
.
There's also the fact that you have a bug in your code - SubTest increments TopTest
's count and not its own. The title of your question says "subclass counter", but since you're counting in __init__()
I assume you're looking for an instance counter (to count subclasses I'm fairly certain you'd need to use metaclasses). This is a perfect use case for self.__class__
, a property which contains an object's class! In order to use it:
def __init__(self):
self.attr1 = self.__class__.count
self.attr2 = self.__class__.count + 1
self.__class__.count += 2
Using that, SubTest.count
will be incremented instead of TopTest.count
when you call SubTest()
.
It looks like you want to keep a counter for each instance of each subclass of TopTest
, but you do not want to repeat yourself by declaring a new count
class variable for each subclass. You can achieve this using a Metaclass:
class TestMeta(type):
def __new__(cls, name, bases, attrs):
new_class = super().__new__(cls, name, bases, attrs)
new_class.count = 0
return new_class
class TopTest(AttrDisplay, metaclass=TestMeta):
def __init__(self):
self.attr1 = self.count
self.attr2 = self.count + 1
self.increment_count(2)
@classmethod
def increment_count(cls, val):
cls.count += val
class SubTest(TopTest):
pass
The count
attribute of your x
and y
objects should now be independent, and subsequent instances of TopTest
and SubTest
will increment the count
:
>>> x, y = TopTest(), SubTest()
>>> x.attr2
1
>>> y.attr2
1
>>> y2 = SubTest()
>>> y2.attr2
3
However, metaclasses can be confusing and should only be used if they are truly necessary. In your particular case it would be much simpler just to re-define the count
class attribute for every subclass of TopTest
:
class SubTest(TopTest):
count = 0
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With