Dans cette mission vous allez étendre votre logiciel de lecture de musique (Mission 8), en utilisant le concept de l'héritage, pour pouvoir traiter d'autres types de médias que les chansons, comme les vidéos, livres audio, etc.
A l'issue de cette mission, chacun d'entre vous :
pourra masquer les variables d'instance d'une classe et écrire des méthodes pour y accéder;
aura appris la notion de variable de classe et son utilité;
pourra expliquer en ses propre mots et illustrer par l'exemple les principes de l'héritage en Python:
* l'héritage d'une classe; * la redéfinition de méthodes; * l'utilisation de ``self`` et ``super()``;
sera capable d'utiliser l'héritage pour étendre un programme Python;
pourra utiliser les méthodes magiques comme
* ``__init__`` pour initialiser les objets d'une classe; * ``__str__`` pour retourner une représentation textuelle d'un instance d'une classe; * ``__eq__`` pour définir l'égalité entres objets d'une classe.
La matière relative à cette mission est décrite dans les sections suivantes de la partie Objects du syllabus en ligne:
ainsi que les annexes:
Les questions à choix multiples de cette mission sont accessibles en ligne depuis https://inginious.info.ucl.ac.be/course/LSINF1101-PYTHON/mission_9_QCM
Considérez une classe Pair :
class Pair: def __init__(self, x, y): self.a = x self.b = y def __str__(self): return str(self.a) + ", " + str(self.b)
Maintenant considérez le code suivant :
p1 = Pair(9, 42) p2 = Pair(9, 42); print(p1 == p2)
La dernière instruction affichera False. (Pourquoi?)
Ajoutez une méthode __eq__(self, p) à la classe Pair qui compare la valeur de deux paires, de manière à ce que print(p1 == p2) imprimera True au lieu de False.
Dans l'implémentation de votre méthode pensez également à gérer le cas où p == None.
L'héritage est un principe de base de la programmation orientée objet. Considérons les classes A, B, C et D ci-dessous :
class A : def m1(self) : print("A 1") def m2(self) : print("A 2") def m3(self) : self.m1() # appel à la méthode m1 sur la même instance def nom(self) : return "A" class B(A) : def m2(self): print("B 2") class C(A): def m1(self) : print("C 1") def nom(self): return "C" class D(C) : def m2(self) : print("D 2")
Considérant ces quatre classes, on vous demande de :
a = A() print(a.nom()) a.m1() a.m2() a.m3() b = B() print(b.nom()) b.m1() b.m2() b.m3() c = C() print(c.nom()) c.m1() c.m2() c.m3() d = D() print(d.nom()) d.m1() d.m2() d.m3()
class E : def m(self) : print("E 1") def n(self) : print("E 2") def p(self) : self.n() # appel à la méthode n sur la même instance class F(E) : def q(self) : print("F 1") def n(self) : super().n() # appeler la méthode définie sur la classe mère print("F 2") def r(self) : self.m() # appel à la méthode m sur la même instance
Expliquer ce qui sera affiché lors de l'exécution des instructions Python suivantes :
f = F() f.q() f.m() f.r() f.n() f.p()
Considérons la classe Figure reprise ci-dessous :
class Figure: def __init__(self,x,y,visible=False) : """ @pre x, y sont des entiers représentant des positions sur l'écran @post Une figure a été créée avec centre de gravité aux coordonnées x,y. Cette figure n'est initialement pas visible. """ self.x = x self.y = y self.__visible = visible def est_visible(self) : """ @pre - @post a retourné la visibilité de cette figure """ return self.__visible def surface(self) : """ @pre - @post la surface (un float) de la figure a été calculé et retournée """ pass # code non fourni
Cette classe Figure est la classe mère d'un ensemble de classes permettant de représenter des figures géométriques. Chaque figure géométrique est placée à une position (x,y) (centre de gravité) sur l'écran et la classe contient des variables d'instance et des méthodes permettant de manipuler cette figure géométrique (notamment des méthodes permettant d'afficher la figure à l'écran, mais ces méthodes ne sont pas reprises dans les extraits présentés dans cet exercice). Parmi ces figures géométriques, on trouve notamment la classe Rectangle qui hérite de la classe Figure et dont un fragment est repris ci-dessous :
class Rectangle(Figure): def __init__(self,longueur,largeur,x,y) : """ @pre longueur et largeur sont des entiers positifs x, y sont des entiers représentant des positions sur l'écran @post un rectangle dont le centre de gravite est en x,y et ayant comme longueur lo et comme largeur la a été créé """ super().__init__(x,y) self.longueur = longueur self.largeur = largeur def __str__(self) : return str((self.longueur,self.largeur,self.x,self.y,self.est_visible())) >>> r = Rectangle(10,20,0,0) >>> print(r) (10, 20, 0, 0, False)
Maintenant expliquez :
Dans la classe Rectangle, faut-il redéfinir les méthodes suivantes (si oui, écrivez le code de la nouvelle méthode - si non, expliquez pourquoi ):
Comment feriez-vous maintenant pour définir une classe Carre qui étend la classe Rectangle et permet de représenter un carré ?
Définissez une méthode __eq__ pour la classe Figure, telle que deux figures sont égales si leur surface est égale.
Que se passe-t il si on veut comparer deux rectangles ayant la même surface? Par exemple:
>>> r1 = Rectangle(10,40,0,0) >>> r2 = Rectangle(2,200,0,0) >>> r1 == r2
Que se passe-t il si on veut comparer un rectangle avec un carré ayant la même surface? Par exemple:
>>> r = Rectangle(10,40,0,0) >>> c = Carre(20,0,0) >>> print(r == c)
Et voici finalement une dernière question pour illustrer l'utilisation d'une variable de classe.