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.
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,
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 :
Signalons que super(C,D,self).affiche(), super(D,C).affiche(self) et super(B,C,D).affiche(self) sont incorrects.
Nous donnons, dans ce qui suit, plusieurs exemples, pour mieux aider à comprendre l'héritage en Python.
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 :
>>> ex1=M()
M
>>> ex2=F()
F
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.
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 :
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