Nov 23, 2024

Wiki

Python

Aide

edit SideBar

Search

Le Polymorphisme

La théorie

Un premier exemple

Considérons

  >>> class Voiture :
  ...     type = "Voiture"
  ...     def affiche_type(self) :
  ...         print self.type
  ...     def utilise_type(self) :
  ...         self.affiche_type()

  >>> class Toyota(Voiture) :
  ...     def affiche_type(self) :
  ...         print "Toyota et self.type

La classe Voiture et sa classe fille Toyota ont toutes deux une méthode affiche_type().

Lorsque l'on instancie un objet toto de type Toyota, et qu'on lui demande :

  >>> toto = Toyota()
  >>> toto.utilise_type()

alors, la méthode utilise_type() de la classe mère est appelée, qui appelle la méthode affiche_type().

Dans l'exemple précédent, quelle méthode affiche_type() sera choisie ? Celle de la classe d’instanciation de l’objet : toto a été instancé comme Toyota, c’est donc Toyota.affiche_type(toto) qui s’exécutera.

La règle de résolution des noms de variables

Python respecte en effet la règle suivante de résolution des noms de variable : lorsqu'on lui demande d'utiliser la valeur d'une variable X,

  • il commence par rechercher ce nom dans l'espace local (le plus « interne », en quelque sorte).
  • Si une variable alpha est trouvée dans l'espace local, c'est celle-là qui est utilisée, et la recherche s'arrête.
  • Sinon, Python examine l'espace de noms de la structure parente, puis celui de la structure grand-parente, et ainsi de suite jusqu'au niveau principal du programme.

Invoquer les méthodes d'une classe parente

Le polymorphisme comprend la possibilité de surcharger des méthodes et d'invoquer les méthodes d'une classe parente.

Le type super est utilisé pour remonter les dérivations.

  >>> class A(object):
  ...     def affiche(self):
  ...         print "A"
  ...

Noter le object : il est nécessaire pour utiliser super dans la suite.

  >>> class B(A):
  ...     def affiche(self):
  ...         print "B"
  ... 
  >>> class C(B):
  ...     def affiche(self):
  ...         print "C"
  ... 
  >>> class D(C):
  ...     def affiche(self):
  ...         print "D"
  ...         super(D,self).affiche()
  ...         super(C,D).affiche(self)
  ...         super(B,D).affiche(self)
  ...         super(B,C).affiche(self)
  ... 

  >>> d=D()
  >>> d.affiche()
  D
  C
  B
  A
  A

Il y a donc deux écritures possibles :

super(classe,instance)
Par exemple super(D,self).affiche() : renvoie un objet qui donne accès aux méthodes de la classe juste au-dessus.
super(classe,sous-classe)
Par exemple super(C,D).affiche(self) : renvoie un objet qui donne accès aux méthodes de la classe donnée, sous leurs formes détachées (self doit alors être passé en premier argument.

Signalons que super(C,D,self).affiche(), super(D,C).affiche(self) et super(B,C,D).affiche(self) sont incorrects.

Exemples et utilisation avancée

Nous donnons, dans ce qui suit, plusieurs exemples, pour mieux aider à comprendre l'héritage en Python.

Première illustration

Supposons qu'une classe fille F possède une méthode de même nom m qu'une classe mère M :

  >>> class M:
  ...     def m(self):
  ...         print "M"
  ... 
  >>> class F(M):
  ...     def m(self):
  ...         print "F"
  ... 

Alors :

  • Si l'on instancie un objet de la classe mère, la méthode m sera celle de la classe M, et elle seule :
  >>> ex1=M()
>>> ex1.m()
  M
  • Si l'on instancie un objet de la classe fille, la méthode m sera celle de la classe F, et elle seule :
  >>> ex2=F()
>>> ex2.m()
  F

Deuxième illustration

Pour obtenir la méthode d'une classe autre que celle d'instantiation, l'expliciter :

  >>> ex2=F()
  >>> M.m(ex2)
  M

Ainsi, si l'on souhaite qu'une méthode d'une classe parente soit appelée, il faut le dire (quand bien même les deux méthodes portent le même nom) :

  >>> class M:
  ...     def m(self):
  ...         print "M"
  ... 

  >>> class F(M):
  ...     def m(self):
  ...         M.m(self)
  ...         print "F"
  ... 

  >>> ex3=F()
  >>> ex3.m()
  M
  F

Cela a une importance cruciale pour les méthodes dites spéciales (constructeur, destructeur) : il faut explicitement appeler les constructeurs des superclasses.

Troisième illustration

Lorsque, dans la définition d'une classe, on souhaite faire appel à une méthode définie dans une autre classe, on doit lui transmettre la référence de l'instance (self) comme premier argument.

Cela reste valable pour les méthodes spéciales :

  >>> class M:
  ...     def __init__(self,a):
  ...         print a
  ... 

  >>> class F(M):
  ...     def __init__(self,a,b):
  ...         M.__init__(self,a)
  ...         print b 
  ... 

Dans ce qui précède, on souhaite que le constructeur de la classe fille fasse appel à celui de la classe mère :

  • On doit donc faire référence explicitement au constructeur de la classe mère (M.__init__ dans F.__init__).
  • La référence de l'instance (self) doit être transmise (d'où le M.__init__(self,...)).

Quatrième illustration

Quand la classe mère est appelée par une méthode d'une classe fille, le self utilisé dans la classe mère est une instance de la classe fille :

  >>> class M:
  ...     def m(self):
  ...         print self
  ... 

  >>> class F(M):
  ...     def m(self):
  ...         M.m(self)
  ...         print self
  ... 

  >>> ex = F()
  >>> ex.m()
  <__main__.F instance at 0xb78daecc>
  <__main__.F instance at 0xb78daecc>

La preuve :

  >>> class M:
  ...     def affiche(self):
  ...         print "M"
  ...     def m(self):
  ...         self.affiche()
  ... 

  >>> class F(M):
  ...     def affiche(self):
  ...         print "F"
  ...     def m(self):
  ...         M.m(self)
  ... 

La méthode m de F se contente de lancer la méthode M.m, qui renvoie le résultat de la méthode affiche appliqué à l'instance. Comme, dans ce qui suit, notre instance est un objet F :

  >>> ex = F()

Alors, le self.affiche exécuté dans la classe M correspondra à F.affiche...

  >>> ex.m()
  F

Page Actions

Recent Changes

Group & Page

Back Links