The question is not really specific to Kivy but to Python and to the POO (Object Oriented Programming) paradigm in general.
The short answer would be that although the __init__
method is sometimes called "constructor" it is really just an initializer of the instance, it is generally used to initialize the attributes of the object that we create and it is automatically executed only instantiate the class In your case, as within it we call the method showtext
the text is already loaded in the label when our app starts.
super
is used in this case to call the initializer of the parent class BoxLayout
. In this case it is equivalent to BoxLayout.__init__(self, *args, **kwargs)
.
The long answer is then all of it is oriented to Python 3, although everything is applicable to the new style classes of Python 2 (explicitly derived from object
):
If we get strict, the term 'constructor' in Python can be somewhat ambiguous. When we create an object, two special methods are involved, the first thing called is the __new__
method and then the __init__
method is called:
-
__new__
: basically what it does is return a valid instance of the class. Only builds an object of its class and returns it , so it should be considered the true "constructor" of the class. Receive a reference to the class as the first argument ( cls
).
Overwriting this method is not as common as in the case of __init__
, generally, its use is to customize the instantiation of the class, for example it is one of the ways to create a singleton:
class Singleton:
_instance = None
def __new__(cls, *args, **kwargs):
if not cls._instance:
cls._instance = super().__new__(cls, *args, **kwargs)
return cls._instance
>>> a = Singleton()
>>> b = Singleton()
>>> a
<__main__.Singleton object at 0x7f79ba4022e8>
>>> b
<__main__.Singleton object at 0x7f79ba4022e8>
If we overwrite the method we must get the new instance by calling the __new__
of the parent (which comes from the class object
from which all kinds of new style derives), for example:
instance = super().__new__(cls, *args, **kwargs)
-
__init__
: if __new__
returns an instance of its class (normal) then implicitly executes the __init__
method to which the newly created instance passes as the first argument ( self
by convention). If __new__
does not return an instance of the class is not called and must be done explicitly. Some of its features are:
- It can not be called again.
- Can not return anything.
- You can receive parameters that are normally used to initialize instance attributes.
- It's optional.
In many languages both methods are united in one and is considered a constructor. You can find dialectical fights over there, there is an important tendency to call constructor at __init__
even if it is not strictly correct.
The fundamental goal of __init__
is to initialize the attributes of the object that we created:
class Círculo:
pi = 3.1415
def __init__(self, radio: float):
self.radio = radio
def obtener_área(self) -> float:
print(self.pi * self.radio ** 2)
>>> c = Círculo(4)
>>> c.obtener_área()
50.264
The __init__
in this case creates and initializes the instance attribute radio
, owned by every object Círculo
and that differentiates it from other circles.
We can create an attribute outside the __init__
or any method as you may have observed, this is the case of pi
in the previous example. There is a substantial difference in both cases, the attribute pi
is an attribute that belongs to the class and shared by all the instances of the class that we do. radio
instead is created by __init__
for the instance, belongs to the object exclusively by initializing it giving it differentiating properties. For more information, the following question may be helpful:
Difference between instance attributes and class attributes
To finish this, talk about super
since it is commonly used in the Initializer of the class, how in the example you show. This is because when defining our own initializer we overwrite the __init__
of the parent class. For our class to inherit all the characteristics of its parent implemented in its initializer (which we have overwritten) it is therefore necessary to call it explicitly. This is precisely what super(MyWidget, self).__init__()
.
In general it allows to explicitly reference the parent class , so it allows delegating the call of a method to the parent class (or classes).
class A():
def __init__(self):
print("Soy el __init__ de la clase A")
class B(A):
def __init__(self):
super(B, self).__init__()
print ("Soy el __init__ de la clase B")
obj = B()
Exit:
I am the __init__ of class A
I'm the __init__ of class B
In cases of multiple inheritance, where super
has its true potential since it will search for the method among the parent classes in a certain order ( Method Resolution Order ), but this is already very away from the question.
Note: in Python 3 it is not necessary to pass the references to the class and the instance to super
: super().__init__()