Partage
  • Partager sur Facebook
  • Partager sur Twitter

Construire un objet avec héritage multiple ?

    17 octobre 2023 à 19:15:22

    Salut tout le monde !

    Est-ce qu'il y en a ici qui savent comment construire un objet qui utilise l'héritage multiple ?

    Voici un exemple de code qui fonctionne mais je ne suis pas sûr que l'implémentation soit correcte :

    class A:
        def __init__(self, a):
            print("A ctor called!")
            self.a_ = a
    
        def printa(self):
            print(self.a_)
    
    
    class B:
        def __init__(self, b):
            print("B ctor called!")
            self.b_ = b
        
        def printb(self):
            print(self.b_)
    
    
    class C(A, B):
        def __init__(self, a, b, c):
            self.a_ = a
            self.b_ = b
            self.c_ = c
    
    c = C(11, 22, 33)
    c.printb()

    Output : 22

    Ici c réussit bien à appeler la méthode printb() de la classe parent B et affiche 22 comme prévu.

    Mon problème est que la construction de la classe C n'appelle pas du tout la construction des classes A et B.

    Est-ce que cela est normal ?

    En principe pour initialiser les attributs a_ et b_ j'aurais aimé utiliser la fonction super() mais je ne sais pas comment elle fonctionne avec l'héritage multiple...

    Est-ce qu'un pro saurait comment il faut faire ?

    Merci d'avance ! :pirate:



    -
    Edité par ThomasAirain 17 octobre 2023 à 19:36:51

    • Partager sur Facebook
    • Partager sur Twitter
      17 octobre 2023 à 22:34:58

      Dans votre cas, difficile de ne pas appeler explicitement l'__init__ de A avec a et celui de B avec b et super ne va pas beaucoup aider.
      • Partager sur Facebook
      • Partager sur Twitter
        18 octobre 2023 à 11:02:06

        Bonjour,

        Exemple avec 2 classes:

        class A:
            def __init__(self, a):
                self.a = a
        
            def __repr__(self):
                return self.a
        
        class B(A):
            def __init__(self, b, a):
                A.__init__(self, a)
                self.b = b
        
            def __repr__(self):
                return self.b + " " + A.__repr__(self)
        
        b = B('b','a')
        print(b)



        • Partager sur Facebook
        • Partager sur Twitter
          17 novembre 2023 à 9:34:47

          Souvent la solution à ce genre de problème va être d'utiliser des arguments nommés uniquement dans les initialiseurs, afin de permettre d'y piocher ce que l'on veut. Et chaque classe doit être consciente qu'il peut y avoir une classe au-dessus dans la hiérarchie et donc lui donner les arguments excédentaires. C'est un contrat établi entre les classes.

          class A:
              def __init__(self, *, a, **kwargs):
                  super().__init__(**kwargs)
                  print("A ctor called!")
                  self.a = a
          
              def printa(self):
                  print(self.a)
          
          
          class B:
              def __init__(self, *, b, **kwargs):
                  super().__init__(**kwargs)
                  print("B ctor called!")
                  self.b = b
          
              def printb(self):
                  print(self.b)
          
          
          class C(A, B):
              def __init__(self, *, c, **kwargs):
                  super().__init__(**kwargs)
                  print("C ctor called!")
                  self.c = c
          
              def printc(self):
                  print(self.c)
          
          
          obj = C(a=11, b=22, c=33)
          obj.printa()
          obj.printb()
          obj.printc()
          

          -
          Edité par entwanne 17 novembre 2023 à 9:35:10

          • Partager sur Facebook
          • Partager sur Twitter
            18 novembre 2023 à 17:14:07

            entwanne a écrit:

            chaque classe doit être consciente qu'il peut y avoir une classe au-dessus dans la hiérarchie et donc lui donner les arguments excédentaires. C'est un contrat établi entre les classes.

            D'où une préférence à un appel aux __init__ de chaque classe plutôt que d'utiliser super: plus facile d'imaginer ce qu'il va se passer en étant explicite qu'avec une méthode implicite.
            • Partager sur Facebook
            • Partager sur Twitter
              19 novembre 2023 à 11:31:35

              Pas vraiment, c'est même plutôt limitant (ça contraint les constructions possibles à partir de ces classes de base). Je préfère me reposer sur les mécanismes de Python que d'inventer mon propre ordre de résolution qui risque de casser à la moindre petite modification.

              • Partager sur Facebook
              • Partager sur Twitter
                20 novembre 2023 à 18:46:08

                entwanne a écrit:

                ...Je préfère me reposer sur les mécanismes de Python que d'inventer mon propre ordre de résolution qui risque de casser à la moindre petite modification.

                Appeler explicitement l'__init__ d'un parent est aussi un mécanisme de python. Passer par super a aussi ses limites. 

                • Partager sur Facebook
                • Partager sur Twitter
                  21 novembre 2023 à 10:52:30

                  Oui, on peut aussi choisir de construire les objets en appelant __new__ et se passer d'__init__, c'est là encore un mécanisme de Python.

                  Pourtant, il vaut mieux écrire ses classes de façon à correspondre au comportement standard (ordre de résolution des méthodes) plutôt que d'avoir à le tordre.

                  • Partager sur Facebook
                  • Partager sur Twitter
                    21 novembre 2023 à 11:53:53

                    entwanne a écrit:

                    Oui, on peut aussi choisir de construire les objets en appelant __new__ et se passer d'__init__, c'est là encore un mécanisme de Python.

                    Pourtant, il vaut mieux écrire ses classes de façon à correspondre au comportement standard (ordre de résolution des méthodes) plutôt que d'avoir à le tordre.


                    On fait ce qu'on veut pour autant qu'on ait de bonnes raisons de le faire (et l'ignorance n'est pas une de ces raisons). On peut donc utiliser __new__ à la place de... si tant est qu'on soit capable de le justifier tout comme on peut se passer de super()...

                    • Partager sur Facebook
                    • Partager sur Twitter
                      26 novembre 2023 à 12:45:14

                      Bonjour,

                      La première question qu'on pourrait se poser est : Dans quel but utiliser l'héritage multiple ?

                      J'invite à lire la page Wikipedia sur le sujet.

                      Je sais qu'en Django on utilise souvent les "mixin" qui ont un intérêt mais dans la réalité, en tant que développeur, j'en ai très rarement eu le besoin, et quand je pensais l'avoir, il se trouvait que cela m'apportait des problèmes par la suite dans mes développements.

                      • Partager sur Facebook
                      • Partager sur Twitter

                      Celui qui trouve sans chercher est celui qui a longtemps cherché sans trouver.(Bachelard)
                      La connaissance s'acquiert par l'expérience, tout le reste n'est que de l'information.(Einstein)

                      Construire un objet avec héritage multiple ?

                      × Après avoir cliqué sur "Répondre" vous serez invité à vous connecter pour que votre message soit publié.
                      • Editeur
                      • Markdown