瀏覽代碼

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

master
Dimitri Merejkowsky 4 年之前
父節點
當前提交
fd81d07fa4
共有 4 個文件被更改,包括 165 次插入44 次删除
  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 查看文件

@@ -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 查看文件

@@ -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 查看文件

@@ -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 查看文件

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