'Changing class attribute value before and after the instance one [duplicate]

I'm trying to figure out how the class/instance variables work in Python.

Setting aside the obvious facts like mutable objects (like lists) defined as class variables are shared between instances etc., I've messed around with something different. No better way to visualize it than an example:

class Foo:
    x = 0


f = Foo()
print(f"f.x = {f.x}")
print(f"id(Foo.x), id(f.x) {id(Foo.x)} {id(f.x)}")

print("Setting Foo.x to 5")
Foo.x = 5
print(f"f.x = {f.x}")
print(f"id(Foo.x), id(f.x) {id(Foo.x)} {id(f.x)}")

print("Setting f.x to 3")
f.x = 3
print(f"f.x = {f.x}")
print(f"id(Foo.x), id(f.x) {id(Foo.x)} {id(f.x)}")

print("Setting Foo.x to 6")
Foo.x = 6
print(f"f.x = {f.x}")
print(f"id(Foo.x), id(f.x) {id(Foo.x)} {id(f.x)}")

This is the output:

f.x = 0
id(Foo.x), id(f.x) 1711594340560 1711594340560
Setting Foo.x to 5
f.x = 5
id(Foo.x), id(f.x) 1711594340720 1711594340720
Setting f.x to 3
f.x = 3
id(Foo.x), id(f.x) 1711594340720 1711594340656
Setting Foo.x to 6
f.x = 3
id(Foo.x), id(f.x) 1711594340752 1711594340656

So let's walk it line by line:

  1. At first f.x will be 0, obviously
  2. Now setting Foo.x to 5 changes the f.x value to 5 - nothing new here, at least for Python devs
  3. f.x is set to 3, therefore f.x value will be 3, again correct
  4. Now Foo.x is being set to 6, I would expect f.x to be changed to 6 as well

I've printed the id()s of the attributes to see what's going on. So, what happens is, each time x value (on Foo or f) gets changed, it's a new object, new id. I understand that. But why in the case of first Foo.x = 5 change (when Foo.x and f.x have the same ids), the attribute gets updated both in class and the instance? This won't happen again, when these are two separate objects.

I mean the question is this: how does it happen (or how does Python know) that when Foo.x and f.x are the same immutable object, when I reassign Foo.x, both of them will get changed?



Solution 1:[1]

Instance attributes shadow class attributes. Until you executed f.x = 3, the expression f.x evaluated to Foo.x, because f did not have an instance attribute named x. After the assignment, f.x resolved to the value of the newly created instance attribute.

There is no way to create or change a class attribute directly via an instance, and creating or changing the value of an instance attribute does not affect a class attribute with the same name.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 chepner