# 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__ 
=> 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__ 
* 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:

* Etrange, on s'attend à ce que le 2e CD ait le numéro de série $10001$...

* 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 !

* __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)
```