A colleague of mine wrote code analogous to the following today, asked me to have a look, and it took me a while to spot the mistake:
class A():
def __init__(self):
print('A')
class B(A):
def __init__(self):
super(B).__init__()
b = B()
The problem here is that there's no self
parameter to super()
in B
's constructor. What surprised me is that absolutely nothing happens in this case, i.e. no error, nothing. What does the super
object created by super(B)
contain? As an object, it clearly has a constructor, so that's what gets called, but how is that object related to B
? In particular, why is this valid code and doesn't throw an exception somewhere? Is super(B)
an object with some actual use and what would that be?
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.
Understanding Python super() with __init__() methods When this method is called it allows the class to initialize the attributes of the class. In an inherited subclass, a parent class can be referred with the use of the super() function.
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.
super() does not accept any arguments. One core feature of object-oriented programming languages like Python is inheritance. Inheritance is when a new class uses code from another class to create the new class.
The only thing that causes all these ambiguities is that "why obj = super(B).__init__()
works?". That's because super(B).__self_class__
returns None
and in that case you're calling the None
objects' __init__
like following which returns None:
In [40]: None.__init__()
Regarding the rest of the cases, you can simply check the difference by calling the super
's essential attributes in both cases:
In [36]: class B(A):
def __init__(self):
obj = super(B, self)
print(obj.__class__)
print(obj.__thisclass__)
print(obj.__self_class__)
print(obj.__self__)
....:
In [37]: b = B()
<class 'super'>
<class '__main__.B'>
<class '__main__.B'>
<__main__.B object at 0x7f15a813f940>
In [38]:
In [38]: class B(A):
def __init__(self):
obj = super(B)
print(obj.__class__)
print(obj.__thisclass__)
print(obj.__self_class__)
print(obj.__self__)
....:
In [39]: b = B()
<class 'super'>
<class '__main__.B'>
None
None
For the rest of the things I recommend you to read the documentation thoroughly. https://docs.python.org/3/library/functions.html#super and this article by Raymond Hettinger https://rhettinger.wordpress.com/2011/05/26/super-considered-super/.
Moreover, If you want to know why super(B)
doesn't work outside of the class and generally why calling the super()
without any argument works inside a class you can read This comprehensive answer by Martijn https://stackoverflow.com/a/19609168/2867928.
A short description of the solution:
As mentioned in the comments by @Nathan Vērzemnieks you need to call the initializer once to get the super()
object work. The reason is laid behind the magic of new super
object that is explained in aforementioned links.
In [1]: class A:
...: def __init__(self):
...: print("finally!")
...:
In [2]: class B(A):
...: def __init__(self):
...: sup = super(B)
...: print("Before: {}".format(sup))
...: sup.__init__()
...: print("After: {}".format(sup))
...: sup.__init__()
...:
In [3]: B()
Before: <super: <class 'B'>, NULL>
After: <super: <class 'B'>, <B object>>
finally!
The confusion here comes from the fact that (in a class definition context) super()
gives a bound super
object which then delegates __init__
to its __self_class__
, while super(B)
creates an unbound super
object which, because its __self_class__
is None
, does not delegate.
In [41]: class Test(int):
...: def __init__(self):
...: print(super().__self_class__)
...: print(super().__init__)
...: print(super(Test).__self_class__)
...: print(super(Test).__init__)
...:
In [42]: Test()
<class '__main__.Test'>
<method-wrapper '__init__' of Test object at 0x10835c9c8>
None
<method-wrapper '__init__' of super object at 0x10835c3c8>
So when you call super(B).__init__()
, it creates an unbound super
but then immediately calls __init__
on it; that, because of the magic described in the various links in this other answer, binds that unbound super
. There are no references to it, so it disappears, but that's what's happening under the hood.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With