Reputation: 22234
Please explain why I cannot use super(class, self)
to access the properties defined in the class higher in the class inheritance hierarchy, and how to access them.
According to the document, super(class, self
should be returning a proxy object via which I can access the def name()
in a parent class instance.
Return a proxy object that delegates method calls to a parent or sibling class of type. This is useful for accessing inherited methods that have been overridden in a class.
The object-or-type determines the method resolution order to be searched. The search starts from the class right after the type.
For example, if mro of object-or-type is D -> B -> C -> A -> object and the value of type is B, then super() searches C -> A -> object.
I thought super(Parent)
will give a proxy to the GrandParent object which has the def name()
.
class GrandParent:
def __init__(self):
self._name = "grand parent"
@property
def name(self):
return self._name
class Parent(GrandParent):
def __init__(self):
super().__init__()
self._name = "parent"
@property
def name(self):
return super().name
class Child(Parent):
def __init__(self):
super().__init__()
self._name = "child"
@property
def name(self):
return super(Parent).name
print(Child().name)
---
AttributeError: 'super' object has no attribute 'name'
Without (class, self)
, it returns ... the child property. Obviously I have not understood how self
and super
work in Python. Please suggest what resources to look into to fully understand the behavior and the design.
class GrandParent:
def __init__(self):
self._name = "grand parent"
@property
def name(self):
return self._name
class Parent(GrandParent):
def __init__(self):
super().__init__()
self._name = "parent"
@property
def name(self):
return super().name
class Child(Parent):
def __init__(self):
super().__init__()
self._name = "child"
@property
def name(self):
return super().name
print(Child().name)
---
child
Upvotes: 0
Views: 1900
Reputation: 521994
The self
always refers to one and the same object. When you do super().__init__()
, the self
in the parent's __init__
is your instance of Child
. GrandParent.__init__
just sets an attribute on that object. By chaining all those __init__
s, you're in effect just doing this:
o = object()
o._name = 'grand parent'
o._name = 'parent'
o._name = 'child'
You're just overwriting the _name
attribute, of which there's only one. All the different @property
s just return the value of this one _name
attribute, of which your object only has one, and whose value is 'child'
.
If you want your object to have a separate _name
attribute per parent, you will actually have to create separate attributes. The easiest way is probably with Python's double-underscore name mangling a.k.a. "private attributes":
>>> class A:
... def __init__(self):
... self.__foo = 'bar'
... @property
... def foo(self):
... return self.__foo
...
>>> class B(A):
... def __init__(self):
... super().__init__()
... self.__foo = 'baz'
... @property
... def foo(self):
... return super().foo
...
>>> B().foo
'bar'
>>> vars(B())
{'_A__foo': 'bar', '_B__foo': 'baz'}
The actual attributes are named _A__foo
and _B__foo
and thereby don't conflict with each other.
Upvotes: 2