Bladeren bron

Le chapitre sur l'héritage est prêt

master
Dimitri Merejkowsky 4 jaren geleden
bovenliggende
commit
fd81d07fa4
4 gewijzigde bestanden met toevoegingen van 165 en 44 verwijderingen
  1. +2
    -2
      cours/source/11-classes-01/index.rst
  2. +2
    -2
      cours/source/13-classes-02/index.rst
  3. +160
    -40
      cours/source/18-classes-03/index.rst
  4. +1
    -0
      cours/source/index.rst

+ 2
- 2
cours/source/11-classes-01/index.rst Bestand weergeven

@@ -1,5 +1,5 @@
Chapitre 11 - Classes (1ère partie)
===================================
Chapitre 11 - Introduction aux classes
======================================

Ce qu’on a vu jusqu’ici:



+ 2
- 2
cours/source/13-classes-02/index.rst Bestand weergeven

@@ -1,5 +1,5 @@
Chapitre 13 - Classes (2ème partie)
===================================
Chapitre 13 - Couplage et composition
=====================================

.. toctree::
:maxdepth: 1


+ 160
- 40
cours/source/18-classes-03/index.rst Bestand weergeven

@@ -1,5 +1,5 @@
Chapitre 18 - Classes (3ème partie)
====================================
Chapitre 18 - Héritage
======================

Rappel - composition
---------------------
@@ -29,56 +29,176 @@ Pour rappel::



Héritage
--------
Vocabulaire
-----------

L'héritage décrit une autre relation entre classes, appelée parfois un peu abusivement "partage de code".
Ici on va parler d'héritage, qui décrit une autre relation entre classes, appelée parfois un peu abusivement "partage de code".

Petit détour
++++++++++++
Pour indiquer qu'une classe ``B`` hérite d'une classe ``A``, on écrit ``A`` dans des parenthèses au moment de
déclarer la classe ``B``::

Commencons par une question - qu'est-ce qui ne va pas dans ce code ?::
class A:
...

def faire_le_café():
mettre_café_dans_tasse()
allumer_bouilloire()
attendre_que_ça_bouille()
verser_dans_tasse()
melanger()
class B(A):
...

def faire_le_thé():
mettre_thé_dans_tasse()
allumer_bouilloire()
attendre_que_ça_bouille()
verser_dans_tasse()
laisser_infuser()

Les trois formulations suivantes sont souvent employées:

Le proble est la *duplication* du code. Les lignes de ``allumer_bouilloire()`` à ``verser_dans_tasse()`` sont
identiques.
* A est la classe *parente* de B.
* B *hérite* de A.
* B est une classe *fille* de A.

Du coup:
Utilisation
-----------

* 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
Si une méthode n'est pas trouvée dans la classe courante, Python ira la
chercher dans la classe parente::

Une solution est possible est *d'extraire une fonction*::
class A:
def méthode_dans_a(self):
print("dans A")

class B(A):
def méthode_dans_b(self):
print("dans B")

def faire_chauffer_l_eau():
allumer_bouilloire()
attendre_que_ça_bouille()

b = B()
b.méthode_dans_a()
# Affiche: 'dans B', comme d'habitude

def faire_le_café():
mettre_café_dans_tasse()
faire_chauffer_l_eau()
verser_dans_tasse()
melanger()
b.méthode_dans_a()
# Affiche: 'dans A'

def faire_le_thé():
mettre_thé_dans_tasse()
faire_chauffer_l_eau()
verser_dans_tasse()
laisser_infuser()
Ordre de résolution
--------------------

S'il y a plusieurs classes parentes, Python les remonte toutes une à une.
On dit aussi qu'il y a une *hiérarchie* de classes::

class A:
def méthode_dans_a(self):
print("dans A")

class B(A):
def méthode_dans_b(self):
print("dans B")

class C(B):
def méthode_dans_c(self):
print("dans C")

c = C()
c.méthode_dans_a()
# affiche: 'dans A'

Avec \_\_init\_\_
--------------------

La résolution fonctionne pour toutes les méthodes, y compris ``__init__``::

class A:
def __init__(self):
print("initialisation de A")

class B(A):
...

b = B()
# affiche: "initialisation de A"

Attributs
----------

Même mécanisme pour les attributs::

class A:
def __init__(self):
self.attribut_de_a = 42

class B(A):
...

b = B()
print(b.attribut_de_a)
# affiche: 42

Surcharge
----------

On peut aussi *surcharger* la méthode de la classe parente dans la classe fille::

class A:
def une_méthode(self):
print("je viens de la classe A")

class B(A):
def une_méthode(self):
print("je viens de la classe B")


b = B()
b.une_méthode()
# affiche: "je viens de la classe B'

super()
-------

On peut utiliser ``super()`` pour chercher *explicitement* une méthode dans la classe parente::


class A:
def une_méthode(self):
print("je viens de la classe A")

class B(A):
def une_méthode(self):
super().une_méthode()
print("je viens de la classe B")

b = B()
b.une_méthode()
# affiche:
# je viens de la classe A
# je viens de la classe B

super() et \_\_init\_\_
------------------------

Erreur très courante::

class A:
def __init__(self):
self.attribut_de_a = "bonjour"

class B(A):
def __init__(self):
self.attribut_de_b = 42

b = B()
print(b.attribut_de_b)
# affiche: 42
print(b.attribut_de_a)
# erreur: AttributeError

On a surchargé ``A.__init__()``, du coup l'initialisation de A n'a jamais
été faite.

La plupart du temps, si ``A`` et ``B`` ont de constructeurs, on appellera
``super().__init__()`` dans le constructeur de la classe fille::

class A:
def __init__(self):
self.attribut_de_a = "bonjour"

class B(A):
def __init__(self):
self.attribut_de_b = 42

b = B()
print(b.attribut_de_b)
# affiche: 42
print(b.attribut_de_a)
# affiche: "bonjour"

+ 1
- 0
cours/source/index.rst Bestand weergeven

@@ -34,3 +34,4 @@ remarques.
15-fichiers-et-données-binaires/index
16-interpréteur-interactif/index
17-sockets/index
18-classes-03/index