Changing a class attribute in __init__

I was looking at a question about stack overflow counting class instances? , and Iโ€™m not sure why this solution works, but one with a simple addition doesnโ€™t. I think it is more related to how class and instance variables are stored and accessible.

Here's a code that I think should work, but instead creates 4 for each 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 operator works somewhat (adding happens, but not the destination).

What happens under the hood, so this task is not completed here, and itertools.count appointment of itertools.count succeed in solving other issues?

+5
source share
3 answers

self.num += 1 basically means "take the value of self.num , increase it, and assign self.num .

Searching for an attribute on self will find the class variable if there is no corresponding instance variable. However, the assignment will always be written to the instance variable. Thus, it tries to find the var instance, fail, returns to the var class, gets the value, increments it, and then assigns it to the var instance.

The reason the answer to a related question works is because there is no assignment; they call next() directly in the class variable, its value is mutated, but the name is not reassigned.

+4
source

Integers do not implement __iadd__ (adding in place, for += ), since they are immutable. The interpreter reverts to its standard destination and __add__ instead, so the line:

 self.num += 1 

becomes:

 self.num = self.num + 1 

On the right side, you get foo.num (i.e. 3 ) via self.num , as expected, but the interesting thing here is that assigning the shadow attribute to the instance attribute num class attribute. So the string is actually equivalent:

 self.num = foo.num + 1 # instance attribute equals class attribute plus one 

All instances end with self.num == 4 , and the class remains foo.num == 3 . Instead, I suspect you wanted:

 foo.num += 1 # explicitly update the class attribute 

Alternatively, you can implement it as @classmethod , working more explicitly on the class:

 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 
+5
source

Extended assignment does not update the class variable. He does this:

 tmp = self.num self.num = tmp.__iadd__(1) 

Pay attention to the appointment back to self.num there! That way your class attribute remains untouched. The object.__iadd__() method for integers cannot change a number in place, because integers are immutable, so foo.num never changes.

You need to explicitly refer to the class variable:

 foo.num += 1 

or

 self.__class__.num += 1 
+2
source

All Articles