|
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440 |
- % Programmation avec Python (chapitre 8)
- % Dimitri Merejkowsky
-
- \center \huge Rappels
-
- # Définition et utilisation d'une classe
-
- ```python
- class Counter:
- def __init__(self):
- self.count = 0
-
- def increment(self, times=1):
- self.count += times
-
- >>> counter = Counter()
- >>> counter.increment(times=2)
- >>> counter.count
- 2
- >>> counter.increment()
- >>> counter.count
- 3
- ```
-
- # Vocabulaire
-
- * `counter` est une *instance* de la *classe* `Counter`
- * `increment` est une *méthode d'instance*
- * `count` est un *attribut d'instance*
- * `__init__` est un *constructeur*
-
- # Méthodes et attributs de classe
-
- ```python
- class Car:
- total_number_of_cars = 0
-
- def __init__(self, color="black"):
- self.color = color
- Car.total_number_of_cars += 1
-
- @classmethod
- def print_number_of_cars(cls):
- print(cls.total_number_of_cars,
- "cars have been made")
-
- >>> ford = Car()
- >>> ferrari = Car(color="red")
- >>> Car.print_number_of_cars()
- 2 cars have been made
- ```
-
- #
-
- \center \huge Composition
-
- # Composition
-
- * Quand on met une classe dans une autre.
-
- * Par exemple, le constructeur de la classe A va prendre en paramètre une
- instance de la classe B.
-
- * Introduit un *couplage* entre les classes A et B.
-
- # Example
-
- ```python
- class Authorization:
- def __init__(self, credentials_file):
- ...
- self.password = ...
-
- class Client:
- url = "https://exmple.com"
- def __init__(self, auth)
- self.auth = auth
-
- def make_request(self):
- password = self.auth.password
- requests.get(url, password=password)
- ```
-
- # Couplage 1
-
- Il faut construire une instance d'`Authorization` pour pouvoir
- construire une instance de `Client`
-
-
- ```python
- >>> auth = Authorization("credentials.txt")
- >>> client = Client(auth)
- >>> client.make_request()
- ```
-
- # Couplage 2
-
- Si jamais l'atttribut `password` dans la classe `Authorization` change,
- le code dans `Client.make_request()` devra changer aussi.
-
- ```python
- class Authorization:
- ...
- self.password = ...
-
-
- class Client:
- ...
-
- def make_request(self):
- password = self.auth.password
- ...
- ```
-
- # Conclusion
-
- Prenez le temps d'étudier les relations entre les différentes classes!
-
- Souvent un simple schéma suffira.
-
- #
-
- \center \huge Héritage
-
- # Petit détour
-
- Qu'est-ce qui ne va pas dans ce code?
-
- ```python
- def faire_le_cafe():
- mettre_cafe_dans_tasse()
- allumer_bouilloire()
- attendre_que_ca_bouille()
- verser_dans_tasse()
- melanger()
-
- def faire_le_the():
- mettre_the_dans_tasse()
- allumer_bouilloire()
- attendre_que_ca_bouille()
- verser_dans_tasse()
- laisser_infuser()
- ```
-
- # Duplication
-
- * Les lignes de `allumer_bouilloire()` à `verser_dans_tasse()` sont les mêmes
- * Le code est plus long
- * Si jamais la procédure pour faire chauffer l'eau change, il faudra changer
- le code a deux endroits différents
-
- # Solution: extraire une fonction
-
- ```python
- def faire_chauffer_l_eau():
- allumer_bouilloire()
- attendre_que_ca_bouille()
-
- def faire_le_cafe():
- mettre_cafe_dans_tasse()
- faire_chauffer_l_eau()
- verser_dans_tasse()
- melanger()
-
- def faire_le_the():
- mettre_the_dans_tasse()
- faire_chauffer_l_eau()
- verser_dans_tasse()
- laisser_infuser()
- ```
-
- # Facile à changer
-
- Si maintenant il faut débrancher le grille-pain avant de pouvoir faire chauffer l'eau,
- on a juste à changer une fonction:
-
- ```python
- def faire_chauffer_l_eau():
- debrancher_grille_pain()
- brancher_bouilloire()
- allumer_bouilloire()
- attendre_que_ca_bouille()
- ```
-
- # Note
-
- Notez qu'on a *laissé* la ligne `verser_dans_tasse()` dupliquée.
-
- C'est une duplication par *coïncidence*.
-
- Et ça nous permet de faire ça:
-
- ```python
- def faire_le_the():
- faire_chauffer_l_eau()
- mettre_the_dans_tasse()
- verser_dans_tasse()
- laisser_infuser()
- ```
-
- # Conclusion
-
- Encore une fois, réfléchissez avant d'agir!
-
- #
-
- \center \huge Héritage
-
- # Un autre type de relation entre classes
-
- Si la composition est une relation "has-a", l'héritage décrit
- une relation "is-a".
-
- # Composition
-
- ```python
- class Dog:
- pass
-
- class Person:
- def __init__(self, pet=None):
- self.pet = pet
-
- >>> nestor = Dog()
- >>> john = Person(pet=nestor)
- >>> john.pet
- nestor
- ```
-
- `John` *a* un animal.
-
-
- # Héritage
-
- ```python
- class Animal:
- pass
-
-
- class Dog(Animal):
- pass
-
-
- class Cat(Animal):
- pass
-
- ```
-
- `Dog` et `Cat` *sont* des animaux.
-
- # Vocabulaire
-
- ```python
- class A:
- ...
-
- class B(A):
- ...
- ```
-
- * A est la classe *parente* de B.
- * B *hérite* de A.
- * B est une classe *fille* de A.
-
-
- # Utilisation
-
- * Si une méthode n'est pas trouvée dans la classe courante,
- Python ira la chercher dans la classe parente
-
- ```python
- class A:
- def method_in_a(self):
- print("in a")
-
- class B(A):
- def method_in_b(self):
- print("in b")
-
-
- >>> b = B()
- >>> b.method_in_b()
- 'in b' # comme d'habitude
- >>> b.method_in_a()
- 'in a'
- ```
-
- # Ordre de résolution
-
- S'il y a plusieurs classes parentes, Python les remonte toutes:
-
- ```python
- class A:
- def method_in_a(self):
- print("in a")
-
- class B(A):
- def method_in_b(self):
- ...
-
- class C(B):
- def method_in_c(self):
- ...
-
- >>> c = C()
- >>> c.method_in_a()
- 'in a'
- ```
-
- # Avec `__init__`
-
- La résolution fonctionne pour toutes les méthodes, y compris `__init__`
-
- ```python
- class A:
- def __init__(self):
- print("Building parent")
-
- class B(A):
- ...
-
- >>> b = B()
- Building parent
- ```
-
- # Attributs
-
- Même méchanisme pour les attributs:
-
- ```python
- class A:
- def __init__(self):
- self.a_attribute = 42
-
- class B(A):
- ...
-
- >>> b = B()
- >>> b.a_attribute
- 42
- ```
-
- # Overriding
-
- On peut aussi *écraser* la méthode du parent dans l'enfant:
-
- ```python
- class A:
- def my_method(self):
- print("method in A")
-
- class B(A):
- def my_method(self):
- print("method in B")
-
-
- >>> b = B()
- >>> b.my_method()
- "method in B"
- ```
-
- # super()
-
- Demande à chercher une méthode dans la classe parente
-
- ```python
- class A:
- def a_method(self):
- print("method in A")
-
- class B(A):
- def b_method(self):
- super().a_method()
- print("method in B")
-
- >>> b = B()
- >>> b.b_method()
- method in A
- method in B
- ```
-
- # `super` et `__init__`
-
- Erreur très courante:
-
- ```python
- class A:
- def __init__(self):
- self.a_attribute = "foo"
-
- class B(A):
- def __init__(self):
- self.b_attribute = 42
-
- >>> b = B()
- >>> b.b_attribute
- 42
- >>> b.a_attribute
- AttributeError
- ```
-
- On a écrasé `A.__init__`!
-
- # `super` et `__init__`
-
- ```python
- class A:
- def __init__(self):
- self.a_attribute = "foo"
-
- class B(A):
- def __init__(self):
- super().__init__()
- self.b_attribute = 42
-
- >>> b = B()
- >>> b.b_attribute
- 42
- >>> b.a_attribute
- "foo" # OK
- ```
-
- #
-
-
- \center \huge Atelier
-
-
- # Objectif
-
- *Une autre vision de l'héritage*: on va rajouter une fonctionnalité dans
- notre script marvel, puis on va réduire le code dupliqué.
-
- # Résultats
-
- * Départ: https://github.com/E2L/cours-python/blob/master/sources/marvel/marvel_04.py
-
- \vfill
-
- * Arrivée: prochainement sur `git.e2li.org` :)
|