简体   繁体   中英

Python - how to access instance properties in parent classes with super(<class>, self)?

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

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 aka "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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM