Copy of https://perso.isima.fr/loic/python/exo_heritage.php
tete du loic

 Loïc YON [KIUX]

  • Enseignant-chercheur
  • Référent Formation Continue
  • Responsable des contrats pros ingénieur
  • Référent entrepreneuriat
  • Responsable de la filière F2 ingénieur
  • Secouriste Sauveteur du Travail
mail
loic.yon@isima.fr
phone
(+33 / 0) 4 73 40 50 42
location_on
Institut d'informatique ISIMA
  • twitter
  • linkedin
  • viadeo

Leia, je suis ta mère

 Cette page commence à dater. Son contenu n'est peut-être plus à jour. Contactez-moi si c'est le cas!

Date de première publication : 2022/10/4

L'objectif de ce petit TP est de vous montrer l'héritage en Python.

Héritage simple "usuel"

Écrivons notre premier héritage simple : la classe Fille hérite de la classe Mere :


class Mere:
   def __init__(self, nom):
      self.nom = nom
      print("constructeur mere")

   def print(self):
      print("mere ", self.nom)

class Fille(Mere):
   pass

fille = Fille("coucou")
fille.print()

Que peut-on en déduire ?

Si maintenant, on essaie :


class Fille(Mere):
   def __init__(self):
      print("constructeur fille")

fille = Fille()
fille.print()

Qu'obtient-on ?

Un objet Fille est construit mais ni le constructeur de la classe mère ni l'attribut nom ne sont connus !!!

Il n'y a pas d'appel implicite au constructeur de la classe mère. Il faut donc le faire de manière explicite.Deux syntaxes possibles (une vient du python 2 - celle en commentaire) existent:


class Fille(Mere):
   def __init__(self, nom):
      super().__init__(nom)
      # Mere.__init__(self, nom)
      print("constructeur fille")

Testons la redéfinition de méthode:


class Fille(Mere):
   def __init__(self, nom):
      super().__init__(nom)
      print("constructeur fille")

   def print(self):
      print("fille ", self.nom)

fille = Fille("loulou")
fille.print() 

Vous avez deviné la manière d'appeler la méthode print() de la classe mère à partir de la classe fille ?


class Fille(Mere):
   # ...

   def print(self):
      super().print()
      print("fille ", self.nom)

La classe Fille peut avoir des attributs que la classe Mere n'a pas :


class Fille(Mere):
    def __init__(self, nom):
        super().__init__("aucun")
        self.nom2 = nom
        print("constructeur fille")

    def print(self):
         super().print()
         print("verif ", self.nom)
         print("fille ", self.nom2)

fille = Fille("encore")
fille.print()

Cela peut engendrer des trucs bizarres :


class Mere:
    def __init__(self, nom):
        self.nom = nom
        print("constructeur mere")

    def print(self):
        print("mere ", self.nom)
        print("WTF ", self.nom2)

Cela marche évidemment si l'objet connait l'attribut (cela marche pour un objet fille mais pas pour un objet mère seulement)

En UML, on peut définir un attribut dans une classe mère et une classe fille peut également définir un attribut avec le même nom. Cela signifie que la classe fille bénéficie de deux attributs : un propre et l'autre par héritage... Peut-on avoir cette situation en Python ?


class Fille(Mere):
    def __init__(self, nom):
        super().__init__("aucun")
        self.nom = nom
        print("constructeur fille")

    def print(self):
         super().print()
         print("fille ", self.nom)

fille = Fille("eee")
fille.print()

La réponse est donc NON même s'il n'y a pas de message d'erreur !!! C'est une belle différence avec d'autres langages objets comme le Java ou le C++.


class Mere:
    def __init__(self, nom):
        self.__nom = nom
        print("constructeur mere avec att prive")

    def print(self):
        print("** mere privee", self.__nom)
      
class Fille(Mere):  
    #def __init__(self, nom):
    #    super().__init__(nom)

    def print(self):
         print("** fille private ", self.__nom)

fille = Fille("privacy")
fille.print()

Le comportement est celui auquel on s'attend : l'attribut privé n'est pas accessible dans la classe fille mais je suis sûr que vous avez trouvé comme y avoir accès, n'est-ce pas ?

Si l'attribut n'avait pas été précédé de deux underscores mais d'un seul, l'attribut eut été accessible dans la classe fille, mais pas officiellement de l'extérieur. C'est pour cela que parfois, on dit que les attributs avec un seul underscore sont "protégés".

Héritage des membres de classes

On peut vérifier que les attributs de classes et méthodes de classes sont hérités : attributs et méthodes sont accessibles par les classes filles si la visibilité est correcte.

Si un attribut de classe de la classe fille porte le même nom qu'un attribut de classe de la classe mère, il y a masquage.

Héritage multiple

Lors de l'héritage multiple, le python utilise un algorithme de levée d'ambiguité, appelé C3, et dont le résultat est consultable par la méthode mro() ou __mro__ pour method resolution order (il est conseillé de passer par l'attribut)


class A1:
   def print(self):
      print("A1")

class A2:
   def print(self):
      print("A2")

class A3:
   def print(self):
      print("A3")

class B1(A1,A2):
   def print(self):
      print("B1")

class B3(A3):
   def print(self):
      print("B3")

class C1(B1,B3):
   def print(self):
      print("C1")

c1 = C1()
c1.print()

C1.mro()
C1.__mro__

Vous pouvez mettre en commentaire les méthodes print() pour voir ce que cela donne...

Dans un exemple comme celui-ci, cela ne résoud pas tout :


class X:
   def print(self):
      print("X")
    
class Y:
   def print(self):
      print("Y")
    
class A(X, Y):
   def print(self):
      print("A")
    
class B(Y, X):
   def print(self):
      print("B")

class F(A, B):
   def print(self):
      print("F")