Question 1Dans class Duck(Swimmer, Flyer):, si les deux parents définissent une méthode move, laquelle gagne ?
Héritage multiple et MRO — Hériter de plusieurs parents
Apprends l'héritage multiple en Python. Combine des parents avec class Enfant(A, B):, vois comment l'ordre de résolution des méthodes (MRO) choisit parmi les méthodes de même nom, et vérifie l'ordre avec __mro__.
La dernière fois on a couvert l'héritage simple, la surcharge et super(). Python te permet d'hériter de plusieurs parents en même temps — c'est l'héritage multiple. Cette fois on couvre la syntaxe et la règle qui décide quel parent est appelé quand plusieurs ont le même nom de méthode : l'ordre de résolution des méthodes (MRO).
La syntaxe de base
La syntaxe est simple — liste les classes parentes séparées par des virgules. Écrire class Duck(Animal, Swimmer, Flyer): veut dire que Duck hérite des attributs et méthodes de tous les Animal, Swimmer et Flyer.
Dans l'exemple ci-dessous, Animal porte le nom et le comportement de parole, Swimmer ajoute la natation, Flyer ajoute le vol — et Duck rassemble les trois en une créature capable.
class Animal:
def __init__(self, name):
self.name = name
def speak(self):
pass
class Swimmer:
def swim(self):
return f"{self.name} nage"
class Flyer:
def fly(self):
return f"{self.name} vole"
class Duck(Animal, Swimmer, Flyer): # héritage multiple
def speak(self):
return f"{self.name} fait coin"
duck = Duck("Donald")
print(duck.speak()) # Donald fait coin
print(duck.swim()) # Donald nage
print(duck.fly()) # Donald vole
Duck hérite des attributs et __init__ de Animal, de swim de Swimmer et de fly de Flyer. Seul speak est surchargé par Duck lui-même.Ordre de résolution des méthodes (MRO) — Quel parent gagne ?
Les choses se compliquent quand plusieurs parents ont une méthode de même nom. Si Swimmer et Flyer définissaient tous les deux une méthode move, laquelle s'exécute quand tu appelles move sur une instance Duck ?
Python utilise l'ordre de résolution des méthodes (MRO) — un ordre de recherche fixe — pour parcourir les classes du haut vers le bas et exécuter le premier match. Pour l'héritage multiple, l'ordre est de gauche à droite comme écrit dans class Duck(A, B, C):. Donc la méthode de même nom de A gagne ; si elle n'est pas là, on essaie B, puis C.
class Swimmer:
def move(self):
return "nage"
class Flyer:
def move(self):
return "vole"
class Duck(Swimmer, Flyer): # gauche gagne = Swimmer.move
pass
class Goose(Flyer, Swimmer): # inverse l'ordre = résultat différent
pass
print(Duck().move()) # nage
print(Goose().move()) # vole
Duck(Swimmer, Flyer), Python regarde Swimmer d'abord, puis Flyer. Si les deux définissent move, Swimmer.move gagne. Réordonne les parents et le résultat change.Sur CPython, tu peux lire l'ordre réel via NomDeClasse.__mro__ (le runtime ici est MicroPython, qui n'expose pas l'attribut __mro__, donc le snippet ci-dessous est juste une référence pour ce que tu verrais en Python normal).
# Ce que CPython montrerait
class Swimmer:
pass
class Flyer:
pass
class Duck(Swimmer, Flyer):
pass
for cls in Duck.__mro__:
print(cls.__name__)
# Duck
# Swimmer
# Flyer
# object
C'est quoi cet object à la fin ?
Toutes les classes Python héritent finalement de la classe native object. Même quand tu ne l'écris pas, class Foo: est en interne class Foo(object):. C'est pour ça que __mro__ finit toujours par object.
Héritage en diamant et linéarisation C3
Une autre forme qui revient est l'héritage en diamant : B et C héritent chacun de A, et D hérite à la fois de B et C — dessiner le graphe d'héritage donne une forme littérale de losange (diamond).
A est l'ancêtre commun. B et C héritent chacun de A. D hérite alors à la fois de B et C — les flèches forment un diamant.Python calcule le MRO avec un algorithme appelé linéarisation C3, qui produit l'ordre intelligent « essayer d'abord les descendants (B, C), puis remonter à l'ancêtre commun (A) en dernier ».
class A:
def method(self):
return "A"
class B(A):
def method(self):
return "B"
class C(A):
def method(self):
return "C"
class D(B, C): # diamant
pass
print(D().method()) # B
Dans cet exemple, D().method() retourne "B" parce que le MRO est D → B → C → A. Si B ne définissait pas method, celle de C tournerait ; sans ça, celle de A. « D'abord à gauche, mais l'ancêtre commun en dernier » — c'est la règle pour l'héritage en diamant.
Appeler une méthode d'un parent spécifique par nom de classe
Quand tu veux spécifiquement appeler la méthode d'un parent particulier — pas juste celle qui gagne le MRO — super() ne te donne que la classe suivante dans le MRO, donc tu peux n'appeler qu'au plus une version parente. À la place, utilise ClasseParent.method(self, ...) pour choisir exactement celle que tu veux.
Comme l'appel passe par le côté classe, tu dois passer self toi-même comme premier argument — la seule particularité à retenir.
super() exécute une seule méthode (la suivante dans le MRO). Appeler par nom de classe comme A.hello(self) te laisse cibler un parent spécifique indépendamment du MRO — pratique quand tu veux invoquer plusieurs méthodes parentes en séquence.class A:
def hello(self):
print("hello depuis A")
class B:
def hello(self):
print("hello depuis B")
class C(A, B):
def hello(self):
A.hello(self) # appeler explicitement le hello de A
B.hello(self) # appeler explicitement le hello de B
print("hello depuis C")
C().hello()
# hello depuis A
# hello depuis B
# hello depuis C
Puissant, mais à utiliser avec parcimonie
L'héritage multiple est pratique, mais il faut garder le MRO en tête en permanence pour suivre le comportement — c'est un vrai coût cognitif. En pratique, plutôt que de forcer l'héritage multiple, utiliser un seul parent et garder de petites classes de fonctionnalités comme attributs (composition) est souvent plus clair. Garde l'héritage multiple pour des cas comme mixer des capacités indépendantes comme Swimmer / Flyer.
| Ce que tu veux | Comment l'écrire |
|---|---|
| Hériter de plusieurs parents | class Enfant(A, B): ... |
| Voir quel parent gagne | NomDeClasse.__mro__ (CPython) |
| Appeler seulement le suivant dans le MRO | super().method(...) |
| Cibler un parent spécifique | ClasseParent.method(self, ...) |
Vérification des connaissances
Répondez à chaque question une par une.
Question 2Quelle est la bonne façon de lire l'ordre de résolution des méthodes (MRO) d'une classe ? (CPython)
Question 3En héritage multiple, quelle est la bonne façon d'appeler la méthode d'un parent spécifique par son nom ?