# CP Python S12 - Correction QBF ## Code de Sophie ```python= class Item : def __init__(self,author,title,serial): """ Méthode d'initialisation. @pre author et title sont des valeurs de type String serial est un entier > 0 @post Une instance de la classe est créée, et représente un objet ayant comme auteur author, comme titre title et comme numéro de série serial """ self.__author = author self.__title = title self.__serial = serial def __str__(self): """ Méthode de conversion en string. @pre - @post le string retourné contient une représentation de cet objet sous la forme: [num série] Auteur, Titre """ return "[" + str(self.__serial) + "]" + str(self.__author) + ", " + str(self.__title) class CD(Item): # Variable de classe qui représente serial __serial = 100000 def __init__(self, author, title, seconds): super().__init__(author, title) self.__duree = seconds CD.__serial += self.get_serial() @classmethod def get_serial (cls): return cls.__serial + 1 def __str__(self): return "[" + str(self.__serial) + "]" + str(self.__author) + ", " + str(self.__title) + " (" + str(self.__duree) + " s" + ")" ``` ### Tests ```python item = Item("Guillaume & Sophie", "Python from A to Z", 101) cd = CD("Radiohead","The Bends",2917) cd2 = CD("The Geek","Python is cool",3045) print("Test 1", str(item) == "[101] Guillaume & Sophie, Python from A to Z") print("Test 2:", str(cd) == "[10000] Radiohead, The Bends (2917 s)") print("Test 3:", str(cd2) == "[10001] The Geek, Python is cool (3045 s)") ``` ### Erreurs __1__ ![](https://i.imgur.com/e9ZRfwW.png) => Python râle car à la ligne 30, on fait appel à la méthode `__init__` de la classe-mère, `Item`, qui attend non pas 2, mais 3 arguments. Le 3e argument attendu est `serial`...Ca tombe bien, on a aussi besoin d'un numéro de série pour les CD ! Pourquoi ne pas y placer `CD__serial` ? ```python def __init__(self,author,title, duree): super().__init__(author,title,CD.__serial) # etc ``` __2__ ![](https://i.imgur.com/D3ba8qY.png) * Cool, notre premier test passe! * Malheureusement une autre erreur avec `_CD__author` ("name mangling")...Ca ne vous rappelle rien ? Typiquement le genre d'erreur lorsqu'on ne parvient pas à accéder à des variables d'instance privées! Deux choix s'offrent à nous: 1. Soit on définit des *méthodes accesseurs* dans la classe-mère (`author(self)` et `title(self)` et `serial(self)`) et on les réutilise dans la classe-fille (à la ligne 39, dans `__str__(self)`) ```python def author(self): return self.__author def title(self): return self.__title def serial(self): return self.__serial ``` ```python def __str__(self): return "[" + str(self.serial()) + "] " + str(self.author()) + ", " + str(self.title()) + " (" + str(self.__duree) + " s" + ")" ``` 2. Soit on se rend compte qu'au final les méthodes `__str__(self)` des classes mère et fille se ressemblent tout de même pas mal ?! On décide d'utiliser `super()` pour accéder directement à la méthode `__str__(self)` de la classe-mère (Item) et on la redéfinit (*overriding*) pour `CD` afin d'intégrer également la `duree`. ```python def __str__(self): return super().__str__() + " ({} s)".format(self.__duree) ``` __3__ * Cool, tous nos tests s'éxecutent sans erreur! Malheureusement, le dernier ne passe pas... * En printant la représentation de `cd` et `cd2` on a: ![](https://i.imgur.com/cRZZEUI.png) * Etrange, on s'attend à ce que le 2e CD ait le numéro de série $10001$... ![](https://i.imgur.com/KcO8yCl.png) * Okay, ceci s'explique en décomposant `CD__serial += self.get_serial()`...A chaque nouveau CD, cela revient à dire: `CD.__serial = CD.__serial + CD.__serial +1`. Après 1 CD, on aurait `CD.__serial` = $20001$, après 2 CD, on aurait `CD.__serial` = $20001 + 20001 + 1$...Pas exactement ce qu'on veut 🤨 Pourquoi pas simplement: ```python __serial = 10000 def __init__(self,author,title, duree): super().__init__(author,title,CD.__serial) CD.__serial += 1 self.__duree = duree ``` * BINGO ! ![](https://i.imgur.com/egQJScJ.png) * __NB__ Dans cet exercice, on n'était pas obligé de définir la méthode de classe `get_serial(cls)`. Cela dit, cela peut être utile si l'on souhaite accéder directement à la valeur de `CD.__serial` en dehors de la définition de la classe `CD` :-) Une implémentation correcte serait: ```python @classmethod def get_serial (cls): return cls.__serial ``` ## Correctif entier ```python class Item : def __init__(self,author,title,serial): """ Méthode d'initialisation. @pre author et title sont des valeurs de type String serial est un entier > 0 @post Une instance de la classe est créée, et représente un objet ayant comme auteur author, comme titre title et comme numéro de série serial """ self.__author = author self.__title = title self.__serial = serial def __str__(self): """ Méthode de conversion en string. @pre - @post le string retourné contient une représentation de cet objet sous la forme: [num série] Auteur, Titre """ ### À compléter ### return "[{}] {}, {}".format(self.__serial, self.__author, self.__title) class CD(Item): __serial = 10000 def __init__(self,author,title, duree): super().__init__(author,title,CD.__serial) CD.__serial += 1 self.__duree = duree def __str__(self): return super().__str__() + " ({} s)".format(self.__duree) ```