I was looking at the Stack Overflow question Counting instances of a class?, and I'm not sure why that solution works and one using simple addition doesn't. I guess this is more of a question of how class vs. instance variables are stored and accessed.
Here's the code I think should work, but instead produces 4
for every id
:
class foo():
num = 3 # trying 3 instead of 0 or 1 to make sure the add is working
def __init__(self):
self.num += 1
self.id = self.num
f = foo()
g = foo()
print f.id # 4
print g.id # 4
The self.num +=1
statement is somewhat working (the addition is happening, but not the assignment).
What is happening under the hood that's making this assignment fail here, while the itertools.count
assignment succeeds in the other question's solution?
But be careful, if you want to change a class attribute, you have to do it with the notation ClassName. AttributeName. Otherwise, you will create a new instance variable.
Built-in classes can't be modified, but you can "hide" a built-in class (or any other of course) by one of the same name.
Integers don't implement __iadd__
(in-place add, for +=
), as they're immutable. The interpreter falls back to standard assignment and __add__
instead, so the line:
self.num += 1
becomes:
self.num = self.num + 1
On the right-hand side you get foo.num
(i.e. 3
) via self.num
, as you expected, but the interesting thing here is that assigning to the instance attribute num
shadows the class attribute. So the line is actually equivalent to:
self.num = foo.num + 1 # instance attribute equals class attribute plus one
All instances end up with self.num == 4
and the class remains foo.num == 3
. Instead, I suspect what you wanted is:
foo.num += 1 # explicitly update the class attribute
Alternatively, you could implement it as a @classmethod
, working on the class more explicitly:
class Foo(): # note naming convention
num = 3
def __init__(self):
self.increment()
self.id = self.num # now you're still accessing the class attribute
@classmethod
def increment(cls):
cls.num += 1
self.num += 1
means, basically, 'take the value of self.num
, increment it, and assign to self.num
.
Looking up an attribute on self
will find the class variable if there is no corresponding instance variable. However, assigning will always write to an instance variable. So, this tries to find an instance var, fails, falls back to a class var, gets the value, increments it, then assigns to an instance var.
The reason the answer to the linked question works is because there is no assigning going on; they call next()
directly on the class variable, its value is mutated but the name is not reassigned.
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