What exactly does super () return in Python 3?

From the Python3 documentation, super() "returns a proxy object that delegates method calls to a parent or native type class." What does it 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 the string super().__init__() super() returns the current object, which is then implicitly passed to the superclass method __init__() . Is this accurate or am I missing something?

Simply put, I want to understand the following:

When super().__init__() is executed,

  • What exactly is __init__() passed to and how? We call it on super() , so everything that is returned should be passed to the __init__() method from what I understand about Python so far.
  • Why don't we need to go through self to super().__init__() ?
+7
python inheritance
source share
3 answers

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

This proxy is an object that acts as part of a call to the method of the parent class. This is not the class itself; rather, there is enough information so that you can use it to call the methods of the parent class.

If you call __init__() , you get your own, local, subclass function __init__ . When you call super() , you get this proxy object that redirects you to the methods of the parent class. Thus, when you call super().__init__() , this proxy redirects the call to the parent method __init__ .

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

Is that clear to you?


ANSWERS TO COMMENTS

But this should mean that this proxy object is passed to init () when super () is run. init () right?

Wrong. The proxy object is similar to the package name, for example, calling math.sqrt (). You do not pass the math to sqrt, you use it to indicate which sqrt you are using. If you want to pass init to the proxy, the call will be init (super ()). Of course, this challenge would be semantically ridiculous.

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

No, you do not pass by sc ; this is the result of an object creation call (internal __new__ method), which includes an init call. For __init__ the self object is a new element created for you by the Python runtime system. For most class methods, this first argument (called self convention, this in other languages) is the object that calls the method.

+5
source share

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

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

super call returns a proxy / wrapper object that remembers:

  • Instance calling super()

  • Caller class

  • The class that calls super()

This is absolutely great. super always retrieves the attribute of the next class in the hierarchy (really MRO) that has the attribute you are looking for. Thus, it does not return the current object, but rather and more accurately returns an object that remembers enough information to search for the attributes above in the class hierarchy.


  • What exactly is __init__() passed to and how? We call it on super() , so everything that is returned should be passed to the __init__() method from what I understand about Python so far.

You are almost right. But super loves to play on us. super class defines __getattribute__ , this method is responsible for finding attributes. When you do something like: super().y() , super.__getattribute__ receives a search request for y . When it finds y , it passes an instance that calls super on y . In addition, super has a __get__ method that makes it a descriptor, I omit the details of the descriptors here, refer to the documentation to find out more. This also answers your second question why self not passed explicitly.

* Note: super little different and depends on some magic. For almost all other classes, the behavior is the same. I.e:

 a = A() # A is a class ay() # same as Ay(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: Ay(self) b = B() by() is b # True: returns b not super(), self is b not super() 
+3
source share

I wrote a simple test to find out 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 super null argument super returns an object that stores three things:

  • __self__ stores an object whose name corresponds to the first parameter of the method, even if this name was reassigned.
  • __self_class__ saves its type or itself in the case of a class method.
  • __thisclass__ stores the class in which the method is defined.

(Unfortunately, __thisclass__ was implemented in this way, and did not retrieve the method attribute because it does not allow the use of a form with a null super argument with metaprogramming.)

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

0
source share

All Articles