% Programmation avec Python (chapitre 8) % Dimitri Merejkowsky
\center \huge Rappels
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
counter
est une instance de la classe Counter
increment
est une méthode d’instancecount
est un attribut d’instance__init__
est un constructeurclass 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
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.
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)
Il faut construire une instance d'Authorization
pour pouvoir
construire une instance de Client
>>> auth = Authorization("credentials.txt")
>>> client = Client(auth)
>>> client.make_request()
Si jamais l’atttribut password
dans la classe Authorization
change,
le code dans Client.make_request()
devra changer aussi.
class Authorization:
...
self.password = ...
class Client:
...
def make_request(self):
password = self.auth.password
...
Prenez le temps d'étudier les relations entre les différentes classes!
Souvent un simple schéma suffira.
\center \huge Héritage
Qu’est-ce qui ne va pas dans ce code?
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()
allumer_bouilloire()
à verser_dans_tasse()
sont les mêmesdef 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()
Si maintenant il faut débrancher le grille-pain avant de pouvoir faire chauffer l’eau, on a juste à changer une fonction:
def faire_chauffer_l_eau():
debrancher_grille_pain()
brancher_bouilloire()
allumer_bouilloire()
attendre_que_ca_bouille()
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:
def faire_le_the():
faire_chauffer_l_eau()
mettre_the_dans_tasse()
verser_dans_tasse()
laisser_infuser()
Encore une fois, réfléchissez avant d’agir!
\center \huge Héritage
Si la composition est une relation “has-a”, l’héritage décrit une relation “is-a”.
class Dog:
pass
class Person:
def __init__(self, pet=None):
self.pet = pet
>>> nestor = Dog()
>>> john = Person(pet=nestor)
>>> jonh.pet
nestor
John
a un animal.
class Animal:
pass
class Dog(Animal):
pass
class Cat(Animal):
pass
Dog
et Cat
sont des animaux.
class A:
...
class B(A):
...
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'
S’il y a plusieurs classes parentes, Python les remonte toutes:
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'
__init__
La résolution fonctionne pour toutes les méthodes, y compris __init__
class A:
def __init__(self):
print("Building parent")
class B(A):
...
>>> b = B()
Building parent
Même méchanisme pour les attributs:
class A:
def __init__(self):
self.a_attribute = 42
class B(A):
...
>>> b = B()
>>> b.a_attribute
42
On peut aussi écraser la méthode du parent dans l’enfant:
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"
Demande à chercher une méthode dans la classe parente
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:
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__
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
Une autre vision de l’héritage: on va rajouter une fonctionnalité dans notre script marvel, puis on va réduire le code dupliqué.