Héritage Héritage Création de nouvelles classes à partir de classes déjà définies : class ClasseDerivee(ClasseDeBase): <instruction 1> <instruction 2> . . . <instruction N> Si un attribut n'est pas trouvé dans la définition de la classe dérivée, Python cherche récursivement dans la classe de base etc. Héritage Pour dériver la classe B de la class A : class A: def mA(self): print('A') class B(A): def mB(self): print('B') A mA B mB b = B() b.mB() b.mA() # Les attributs de A sont accessibles Héritage Les classes dérivées peuvent remplacer les méthodes de la classe de base. class A: def m(self): print('A') class B(A): def m(self): print('B') b = B() b.m() # Affiche “B” A m B m Héritage Attention ! En Python les méthodes sont « virtuelles » par défaut. class A: def f(self): self.m() def m(self): print('A') class B(A): def m(self): print('B') b = B() b.f() # Affiche “B” A f m B m Héritage Si jamais une méthode de B veut exécuter la méthode originale de A (chain-up) il faut utiliser la syntaxe alternative pour appeler les méthodes : nomclasse.methode(objet) class A: def met(self): print('A') class B(A): def met(self): A.met(self) print('B') x = B() x.met() # Affiche “A” et “B” Héritage Alternative : la fonction super() class A: def met(self): print('A') class B(A): def met(self): super().met() print('B') x = B() x.met() # Affiche “A” et “B” Héritage On trouve souvent le chain-up dans les méthode d'initialisation : class A: def __init__(self, x): self.x = x class B(A): def __init__(self, x): super().__init__(x) self.foo = 'bar' b = B(5) Ici b.x == 5 et b.foo == 'bar'. Personne prenom nom Héritage class Personne: def __init__(self, prenom, nom): self.prenom = prenom self.nom = nom Professeur cours class Professeur(Personne): def __init__(self, prenom, nom, cours): super().__init__(prenom, nom) self.cours = cours class Etudiant(Personne): def __init__(self, prenom, nom, numero): super().__init__(prenom, nom) self.numero = numero p = Professeur('Pierre', 'Curie', 'Physique') e = Etudiant('Maria', 'Skłodowska', '0123456789') Etudiant numero Héritage multiple class A: foo = 'A' class B: bar = 'B' A B foo bar C class C(A, B): pass print(C.foo, C.bar) # Affiche “A B” Héritage multiple class A: foo = 'A' class B(A): pass class C: foo = 'C' class D(B, C): pass print(D.foo) A foo B C foo D Héritage multiple Python utilise l'algorithme « depth first » : on cherche en profondeur dans une branche avant de passer à la suivante D→B→A→C A foo B C foo D Héritage multiple Problème avec le « diamant » A foo class A: foo = 'A' class B(A): pass class C(A): foo = 'C' class D(B, C): pass print(D.foo) B C foo D # ??? Héritage multiple Problème avec le « diamant ». La recherche « depth first » donne D.foo == A.foo, alors que C a déjà « spécifié » l'attribut foo. Nouvelle règle : une classe est toujours cherchée après toutes ses classes dérivées. D→B→C→A A foo B C foo D Héritage multiple A foo On peut toujours connaître le « Method Resolution Order » avec la méthode de classe .mro(): B C foo D >>> print(D.mro()) (<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>) Polymorphisme Polymorphisme En Python, on s'intéresse aux méthodes implémentées par un objet et non pas à son « type » Duck typing : « If it quacks like a duck, it's a duck » class Chat: def parle(self): print('Miaou') class Chien: def parle(self): print('Wof') def f(x): x.parle() chat = Chat() chien = Chien() f(chat) f(chien) Polymorphisme abstract class Animal { public abstract void parle(); } class Chat extends Animal { @Override public void parle() { System.out.println("Miaou"); } } class Chien extends Animal { @Override public void parle() { System.out.println("Wof"); } } Polymorphisme public class Main { static void f(Animal x) { x.parle(); } public static void main(String[] args) { Animal chat = new Chat(); Animal chien = new Chien(); f(chat); f(chien); } } Polymorphisme class Chat: ... class Chien: ... class Robot: def parle(self): print('Bonjour') def f(x): x.parle() chat = Chat() chien = Chien() robot = Robot() f(chat) f(chien) f(robot) Polymorphisme interface Parlant { void parle(); } abstract class Animal implements Parlant { @Override public abstract void parle(); } class Robot implements Parlant { @Override public void parle() { System.out.println("Bonjour"); } } Polymorphisme public class Main { static void f(Parlant x) { x.parle(); } public static void main(String[] args) { Animal chat = new Chat(); Animal chien = new Chien(); Robot robot = new Robot() ; f(chat); f(chien); f(robot); } } Méthodes statiques et de classe Méthodes statiques Parfois on est intéressé à appeler des méthodes sans avoir une instance particulière = méthodes statiques. class Atome: TP = ['H', 'He', 'Li', 'Be', 'B', 'C', 'N', 'O', 'F', Ne'] def __init__(self, np): self.np = np @staticmethod def affiche_symbole(np): print(TP[np - 1]) # pas de 'self' ! # On appelle la méthode sur la classe ! Atome.affiche_symbole(5) Méthodes de classe On peut avoir des méthodes qui en premier argument ont la classe elle-même au lieu d'une instance particulière. Ces méthodes sont déclarées avec le décorateur @classmethod. Les méthodes de classe sont très utiles pour créer de « factory methods ». class Date: def __init__(self, jour, mois, an): self.jour = jour self.mois = mois self.an = an @classmethod def from_string(cls, s): jour, mois, an = [int(x) for x in s.split('/')] return cls(jour, mois, an) d1 = Date(30, 12, 2000) d2 = Date.from_string('30/12/2000')