What is a builder?

5

is the following code of a simple app in kivy:

from kivy.config import Config
Config.set('kivy', 'keyboard_mode', 'system')

from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.lang import Builder
from kivy.properties import StringProperty


Builder.load_file('design2.kv')

class MyWidget(BoxLayout):
    def __init__(self):
        super(MyWidget, self).__init__()
        self.showtext() #Llamamos al método desde el constructor

    def showtext(self):
        with open("Prueba.txt","r") as f:
            self.ids['Label1'].text = f.read()


class myApp(App):
    def build(self):
        return MyWidget()
    def on_pause(self):
        return True
    def on_resume(self):
        pass


if __name__ in ('__main__', '__android__'):
    myApp().run()

Be the .kv file (although this is not the case):

<MyWidget>:
    BoxLayout:
        Label:
            id: Label1

I wonder about the following code snippet of the first code:

class MyWidget(BoxLayout):
    def __init__(self):
        super(MyWidget, self).__init__()
        self.showtext() #Llamamos al método desde el constructor

As I understand it, def __init__(self): is a constructor. What is a builder for? The other question is about the line: super(MyWidget, self).__init__() What is super ?

    
asked by Mr. Baldan 17.04.2017 в 21:15
source

1 answer

12

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__()

    
answered by 17.04.2017 / 23:51
source