Here is my code.
class A(object):
def __init__(self):
self.a = 1
def x(self):
print("A.x")
def y(self):
print("A.y")
def z(self):
print("A.z")
class B(A):
def __init__(self):
A.__init__(self)
self.a = 2
self.b = 3
def y(self):
print("B.y")
def z(self):
print("B.z")
class C(object):
def __init__(self):
self.a = 4
self.c = 5
def y(self):
print("C.y")
def z(self):
print("C.z")
class D(C, B):
def __init__(self):
C.__init__(self)
B.__init__(self)
self.d = 6
def z(self):
print("D.z")
obj = D()
print(obj.a)
Why does print(obj.a)
return 2
and not 4
? I thought Python scans inputs from left to right. So with that logic it should refer to the superclass C
and find that self.a = 4
and not refer to the superclass B
where self.a = 2
CodePudding user response:
The attribute obj.a
is found directly in the instance namespace, so the MRO is not really involved here.
>>> print(obj.__dict__)
{'a': 2, 'c': 5, 'b': 3, 'd': 6}
If you're asking why the instance namespace contains a=2
and not a=4
, it's because it was set to 4 initially and then overwritten:
C.__init__(self) # sets self.__dict__["a"] = 4
B.__init__(self) # sets self.__dict__["a"] = 2
CodePudding user response:
Why does
print(obj.a)
return2
and not4
?
Because the object obj
can only have one attribute named a
, and its value was most recently set to 2
.
I thought Python scans inputs from left to right.
To determine the class' method resolution order, yes. However, the MRO is only relevant when either implicitly looking for attributes that are missing in the current class, or explicitly passing along the chain via super
.
So with that logic it should refer to the superclass
C
No; when obj.a
is looked up at the end, it doesn't look in any classes at all for the attribute, because the object contains the attribute. It doesn't look in C, B or A. It looks in obj
, finds the attribute, and stops looking. (It does first look at D
, in case it defines some magic that would override the normal process.)
The base classes do not create separate namespaces for attributes. Rather, they are separate objects, whose attributes can be found by the attribute lookup process (and, when they are, those attributes might be automatically converted via the descriptor protocol: e.g. attributes that are functions within the class, will normally become methods when looked up from the instance).
But when e.g. self.a = 2
happens, self
means the same object inside that code that obj
means outside. Assigning an attribute doesn't do any lookup - there's nothing to look up; there's already a perfectly suitable place to attach the attribute. So it just gets attached there. Where it will subsequently be found.
Because the parent classes were initialized explicitly, the order is clear: D.__init__
calls C.__init__
which sets self.a = 4
; then that returns and D.__init__
also calls B.__init__
; that calls A.__init__
, which sets self.a = 1
; then B.__init__
directly sets self.a = 2
; then all the calls return (after setting other attributes). In each case, self
is naming the same object, so it sets the same attribute in the same namespace (i.e. the attributes of that object, treated as a namespace).
and not refer to the superclass
B
whereself.a = 2
Again, they are not separate namespaces (and unlike some other languages, not separate "parts" of the object), so B
isn't a "place where" self.a
can have a different value from the one it has "in" C
. There's only one self
object, with one __dict__
, and one a
(equivalently, __dict__['a']
).