Reputation: 1
I have the following classes:
class A(object):
x = 1
class B(A):
pass
class C(A):
pass
When I print the value of x
from each class I get:
>>>A.x, B.x, C.x
(1,1,1)
Then I assign 2
to B.x
B.x = 2
A.x, B.x, C.x
>>>(1,2,1)
Everything was normal but when I assigned 3
to A.x
I got this :
A.x=3
A.x, B.x, C.x
>>>(3,2,3)
I thought it would return (3,2,1)
.
Upvotes: 10
Views: 414
Reputation: 96324
This is fundamentally how inheritance works in Python: for class-level variables, it first checks' the classes namespace, then the namespace of every class in the method resolution order. So, both B
and C
inherit x
from A
:
In [1]: class A(object):
...: x = 1
...: class B(A):
...: pass
...: class C(A):
...: pass
...:
In [2]: vars(A)
Out[2]:
mappingproxy({'__module__': '__main__',
'x': 1,
'__dict__': <attribute '__dict__' of 'A' objects>,
'__weakref__': <attribute '__weakref__' of 'A' objects>,
'__doc__': None})
In [3]: vars(B)
Out[3]: mappingproxy({'__module__': '__main__', '__doc__': None})
In [4]: vars(C)
Out[4]: mappingproxy({'__module__': '__main__', '__doc__': None})
When you ask for B.x
or C.x
, it looks into that class namespace, doesn't find any "x"
, then tries A
's namespace, finds it, and returns it.
Now, when you assign a variable to B.x = 2
, that adds it to B
's class namespace directly:
In [5]: B.x = 2
...:
In [6]: vars(B)
Out[6]: mappingproxy({'__module__': '__main__', '__doc__': None, 'x': 2})
And similarly, when you assign it to A.x=3
, it overwrites the old value:
In [7]: A.x=3
...:
In [8]: vars(A)
Out[8]:
mappingproxy({'__module__': '__main__',
'x': 3,
'__dict__': <attribute '__dict__' of 'A' objects>,
'__weakref__': <attribute '__weakref__' of 'A' objects>,
'__doc__': None})
In [9]: vars(B)
Out[9]: mappingproxy({'__module__': '__main__', '__doc__': None, 'x': 2})
In [10]: vars(C)
Out[10]: mappingproxy({'__module__': '__main__', '__doc__': None})
So now, same as before, when you look for C.x
, it doesn't find it's own, then it looks for x
inside A
, and finds it.
Note, inheritance works like this with instances too, just it checks the instance namespace first, then the instances class's namespace, then all the namespace of the classes in it's method resolution order.
Upvotes: 17
Reputation: 23
I think it's because of this fact that you did not set "a" field for instance of the "C" class.
Thus it gets its default value from the superclass ("Parent class").
If you set the value of "a" in the c instance, You will get "(3,2,1)".
Upvotes: -1