Because q.b -= 1
creates an instance variable with the name b
, look in your __dict__
:
q.__dict__
{'b': 4, 'x': 5, 'y': 6}
p.__dict__
{'x': 5, 'y': 6}
q.b
is different than a.b
, you've shadowed a.b
after the assignment. Take note that this isn't a Python 3 specific issue, Python 2 also behaves in the same way.
This is clearly stated in the assignment statement section of the Language Reference:
Note: If the object is a class instance and the attribute reference occurs on both sides of the assignment operator, the RHS expression, a.x
can access either an instance attribute or (if no instance attribute exists) a class attribute. The LHS target a.x
is always set as an instance attribute, creating it if necessary. Thus, the two occurrences of a.x
do not necessarily refer to the same attribute: if the RHS expression refers to a class attribute, the LHS creates a new instance attribute as the target of the assignment:
class Cls:
x = 3 # class variable
inst = Cls()
inst.x = inst.x + 1 # writes inst.x as 4 leaving Cls.x as 3
This description does not necessarily apply to descriptor attributes, such as properties created with property()
.