Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What exactly does super() return in Python 3? [duplicate]

From Python3's documentation super() "returns a proxy object that delegates method calls to a parent or sibling class of type." What does that mean?

Suppose I have the following code:

class SuperClass():
    def __init__(self):
        print("__init__ from SuperClass.")
        print("self object id from SuperClass: " + str(id(self)))

class SubClass(SuperClass):
    def __init__(self):
        print("__init__ from SubClass.")
        print("self object id from SubClass: " + str(id(self)))
        super().__init__()


sc = SubClass()

The output I get from this is:

__init__ from SubClass.
self object id from SubClass: 140690611849200
__init__ from SuperClass.
self object id from SuperClass: 140690611849200

This means that in the line super().__init__(), super() is returning the current object which is then implicitly passed to the superclass' __init__() method. Is this accurate or am I missing something here?

To put it simply, I want to understand the following:

When super().__init__() is run,

  1. What exactly is being passed to __init__() and how? We are calling it on super() so whatever this is returning should be getting passed to the __init__() method from what I understand about Python so far.
  2. Why don't we have to pass in self to super().__init__()?
like image 691
Asad Moosvi Avatar asked Jun 23 '17 15:06

Asad Moosvi


People also ask

What does super () return Python?

The super() function is used to give access to methods and properties of a parent or sibling class. The super() function returns an object that represents the parent class.

How does super () work in Python?

The super() function in Python makes class inheritance more manageable and extensible. The function returns a temporary object that allows reference to a parent class by the keyword super. The super() function has two major use cases: To avoid the usage of the super (parent) class explicitly.

What is the purpose of super () __ Init__?

When you initialize a child class in Python, you can call the super(). __init__() method. This initializes the parent class object into the child class. In addition to this, you can add child-specific information to the child object as well.

How does super () method play an important role in inheritance?

Python | super() in single inheritance At a fairly abstract level, super() provides the access to those methods of the super-class (parent class) which have been overridden in a sub-class (child class) that inherits from it.


3 Answers

This means that in the line super().__init__(), super() is returning the current object which is then implicitly passed to the superclass' __init__() method. Is this accurate or am I missing something here?

>>> help(super)  
super() -> same as super(__class__, <first argument>)

super call returns a proxy/wrapper object which remembers:

  • The instance invoking super()

  • The class of the calling object

  • The class that's invoking super()

This is perfectly sound. super always fetches the attribute of the next class in the hierarchy ( really the MRO) that has the attribute that you're looking for. So it's not returning the current object, but rather and more accurately, it returns an object that remembers enough information to search for attributes higher in the class hierarchy.


  1. What exactly is being passed to __init__() and how? We are calling it on super() so whatever this is returning should be getting passed to the __init__() method from what I understand about Python so far.

You're almost right. But super loves to play tricks on us. super class defines __getattribute__, this method is responsible for attribute search. When you do something like: super().y(), super.__getattribute__ gets called searching for y. Once it finds y it passes the instance that's invoking the super call to y. Also, super has __get__ method, which makes it a descriptor, I'll omit the details of descriptors here, refer to the documentation to know more. This answers your second question as well, as to why self isn't passed explicitly.

*Note: super is a little bit different and relies on some magic. Almost for all other classes, the behavior is the same. That is:

a = A()  # A is a class 
a.y()    # same as A.y(a), self is a 

But super is different:

class A:
    def y(self):
        return self
class B(A):
    def y(self)
        return super().y()   # equivalent to: A.y(self)
b = B()
b.y() is b  # True: returns b not super(), self is b not super()
like image 191
direprobs Avatar answered Oct 09 '22 21:10

direprobs


returns a proxy object that delegates method calls to a parent or sibling class of type.

This proxy is an object that acts as the method-calling portion of the parent class. It is not the class itself; rather, it's just enough information so that you can use it to call the parent class methods.

If you call __init__(), you get your own, local, sub-class __init__ function. When you call super(), you get that proxy object, which will redirect you to the parent-class methods. Thus, when you call super().__init__(), that proxy redirects the call to the parent-class __init__ method.

Similarly, if you were to call super().foo, you would get the foo method from the parent class -- again, re-routed by that proxy.

Is that clear to you?

Responses to OP comments

But that must mean that this proxy object is being passed to __init__() when running super().__init__() right?

Wrong. The proxy object is like a package name, such as calling math.sqrt(). You're not passing math to sqrt, you're using it to denote which sqrt you're using. If you wanted to pass the proxy to __init__, the call would be __init__(super()). That call would be semantically ridiculous, of course.

When we have to actually pass in self which is the sc object in my example.

No, you are not passing in sc; that is the result of the object creation call (internal method __new__), which includes an invocation of init. For __init__, the self object is a new item created for you by the Python run-time system. For most class methods, that first argument (called self out of convention, this in other languages) is the object that invoked the method.

like image 20
Prune Avatar answered Oct 09 '22 21:10

Prune


I wrote a simple test to investigate what CPython does for super:

class A:
    pass

class B(A):
    def f(self):
        return super()

    @classmethod
    def g(cls):
        return super()

    def h(selfish):
        selfish = B()
        return super()

class C(B):
    pass

c = C()
for method in 'fgh':
    super_object = getattr(c, method)()
    print(super_object, super_object.__self__, super_object.__self_class__, super_object.__thisclass__)  # (These methods were found using dir.)

The zero-argument super call returns an object that stores three things:

  • __self__ stores the object whose name matches the first parameter of the method—even if that name has been reassigned.
  • __self_class__ stores its type, or itself in the case of a class method.
  • __thisclass__ stores the class in which the method is defined.

(It is unfortunate that __thisclass__ was implemented this way rather than fetching an attribute on the method because it makes it impossible to use the zero-argument form of super with meta-programming.)

The object returned by super implements getattribute, which forwards method calls to the type found in the __mro__ of __self_class__ one step after __thisclass__.

like image 36
Neil G Avatar answered Oct 09 '22 21:10

Neil G