Le guide ultime et définitif sur la programmation orientée objet en Python à l’usage des débutants qui sont rassurés par les textes détaillés qui prennent le temps de tout expliquer. Partie 4.


Un peu de zik, puisque c’est ce qu’on fait maintenant :

Prérequis :

Aujourd’hui nous allons voir ce qui fait toute la puissance de la programmation orientée objet : l’héritage.

Héritage simple

L’héritage est un moyen de factoriser du code, c’est à dire que si on a le même code à deux endroits, l’héritage permet de centraliser ce code à un seul endroit. Ce n’est pas le seul moyen de faire ça. On peut très bien factoriser du code uniquement avec des fonctions. Néanmoins l’héritage a des caractéristiques qui rendent la factorisation très efficace.

Supposons que vous fassiez un jeu vidéo pour apprendre les prières chrétiennes, et que vous ayez une classe par prière :

class AveMaria:
 
 
    texte = ("Je vous salue, Marie pleine de grâce ;",
            "Le Seigneur est avec vous.",
            "Vous êtes bénie entre toutes les femmes",
            "Et Jésus, le fruit de vos entrailles, est béni.",
            "Sainte Marie, Mère de Dieu,",
            "Priez pour nous, pauvres pécheurs,",
            "Maintenant, et à l'heure de notre mort.",
            "Amen.")
 
 
    def prier(self, nombre_de_fois=1):
 
        for x in xrange(nombre_de_fois):
            for ligne in self.texte:
                print ligne
 
 
 
class PaterNoster:
 
 
    texte = ("Notre Père qui es aux cieux",
            "que ton nom soit sanctifié",
            "que ton règne vienne,",
            "que ta volonté soit faite",
            "sur la terre comme au ciel.",
            "Donne-nous aujourd’hui",
            "notre pain de ce jour,",
            "pardonne-nous nos offenses",
            "comme nous pardonnons aussi",
            "à ceux qui nous ont offensés",
            "et ne nous soumets pas à la tentation",
            "mais délivre-nous du mal.",
            "Amen")
 
 
    def prier(self, nombre_de_fois=1):
 
        for x in xrange(nombre_de_fois):
            for ligne in self.texte:
                print ligne
 
 
>>> piere = AveMaria()
>>> piere.prier()
Je vous salue, Marie pleine de grâce ;
Le Seigneur est avec vous.
Vous êtes bénie entre toutes les femmes
Et Jésus, le fruit de vos entrailles, est béni.
Sainte Marie,re de Dieu,
Priez pour nous, pauvres pécheurs,
Maintenant, et à l'heure de notre mort.
Amen.

Ici la méthode prier() est dupliquée. Or, c’est exactement la même. Il n’y a pas de raison de l’écrire deux fois. Nous allons utiliser l’héritage pour centraliser ce code en créant une classe Priere qui contiendra le code commun à toutes les prières :

class Priere:
 
    def prier(self, nombre_de_fois=1):
 
        for x in xrange(nombre_de_fois):
            for ligne in self.texte:
                print ligne

Ensuite, nous allons demander à toutes les classes prières d’hériter de cette classe commune :

class AveMaria(Priere): # <---- cette syntaxe veut dire 'hérite de Priere'
 
    texte = ("Je vous salue, Marie pleine de grâce ;",
            "Le Seigneur est avec vous.",
            "Vous êtes bénie entre toutes les femmes",
            "Et Jésus, le fruit de vos entrailles, est béni.",
            "Sainte Marie, Mère de Dieu,",
            "Priez pour nous, pauvres pécheurs,",
            "Maintenant, et à l'heure de notre mort.",
            "Amen.")
 
 
class PaterNoster(Priere):
 
    texte = ("Notre Père qui es aux cieux",
            "que ton nom soit sanctifié",
            "que ton règne vienne,",
            "que ta volonté soit faite",
            "sur la terre comme au ciel.",
            "Donne-nous aujourd’hui",
            "notre pain de ce jour,",
            "pardonne-nous nos offenses",
            "comme nous pardonnons aussi",
            "à ceux qui nous ont offensés",
            "et ne nous soumets pas à la tentation",
            "mais délivre-nous du mal.",
            "Amen")

Ici, les classes AveMaria et PaterNoster héritent de la classe Priere. On dit qu’elles sont les enfants (ou filles) de Priere. Ou que Priere est la classe parente de AveMaria et PaterNoster

Notez qu’on a retiré la méthode prier() de PaterNoster et AveMaria. Malgré cela, ça marche :

>>> PaterNoster().prier()
Notre Père qui es aux cieux
que ton nom soit sanctifié
que ton règne vienne,
que ta volonté soit faite
sur la terre comme au ciel.
Donne-nous aujourd'hui
notre pain de ce jour,
pardonne-nous nos offenses
comme nous pardonnons aussi
à ceux qui nous ont offensés
et ne nous soumets pas à la tentation
mais délivre-nous du mal.
Amen

Cela marche car quand on hérite d’une classe, le code de cette classe est copié de la classe parente vers la classe enfant. Ainsi, prier() est automatiquement copiée de Priere vers AveMaria et PaterNostre.

Cela marche pour toutes les méthodes, même celles appelées automatiquement. C’est particulièrement utile avec la méthode __init__:

class Priere:
 
    def __init__(self, expiation=False):
        self.expiation = expiation
 
    def prier(self, nombre_de_fois=1):
 
        for x in xrange(nombre_de_fois):
            for ligne in self.texte:
                if self.expiation:
                    print ligne.upper()
                else:
                    print ligne

On rajoute ici un attribut, et il se retrouve dans la classe enfant (si vous êtes dans un shell, n’oubliez pas de réécrire aussi la classe AveMaria à chaque fois, même si elle ne change pas):

>>> priere = AveMaria(expiation=True)
>>> priere.expiation
True
>>> priere.prier()
JE VOUS SALUE, MARIE PLEINE DE GRâCE ;
LE SEIGNEUR EST AVEC VOUS.
VOUS êTES BéNIE ENTRE TOUTES LES FEMMES
ET JéSUS, LE FRUIT DE VOS ENTRAILLES, EST BéNI.
SAINTE MARIE, MèRE DE DIEU,
PRIEZ POUR NOUS, PAUVRES PéCHEURS,
MAINTENANT, ET à L'HEURE DE NOTRE MORT.
AMEN.

L’héritage, c’est juste cela. Pas la peine de chercher un truc compliqué : le code du parent se retrouve dans celui de l’enfant. Cela marche pour les méthodes et les attributs.

Petit apparté sur object

Dans la partie précédente et dans de nombreux codes, vous avez dû voir des classes comme cela :

class MaClass(object): # wtf is this object stuff ?
 
    # code

Il s’agit bel et bien d’héritage, object étant un type buil-in en Python au même titre que les string ou les int :

>>> object
<type 'object'>

La raison de cet héritage bizarre est qu’à partir de Python 2.2, une nouvelle architecture des classes a été introduite qui corrige les problèmes de l’ancienne. Mais pour garder le code compatible, ces nouvelles classes n’ont pas été activées par défaut.

Ainsi, même si vous êtes en Python 2.7, quand vous faites :

class MaClass:
 
    # code

Vous utilisez l’ancien type de classe (old-style class). Pour dire à Python que vous voulez utiliser le nouveau type de classe (new-style class), il faut hériter d’object :

class MaClass(object):
 
    # code

Il n’y a AUCUN intérêt à utiliser les old-style classes. Je l’ai fais en ce début de cours pour éviter d’introduire la notion d’héritage trop tôt.

A partir de maintenant, quand vous créez une nouvelle classe qui n’a pas de parent, faites la TOUJOURS hériter de object.

Des tas de fonctions (par exemple les properties) fonctionnent beaucoup mieux avec les new-style classes. (D’ailleurs à partir de Python 3, elles sont activées par défaut)

Ainsi, notre classe Priere doit ressembler à ça maintenant :

class Priere(object):
 
    def __init__(self, expiation=False):
        self.expiation = expiation
 
    def prier(self, nombre_de_fois=1):
 
        for x in xrange(nombre_de_fois):
            for ligne in self.texte:
                if self.expiation:
                    print ligne.upper()
                else:
                    print ligne

Overriding

Parfois on veut tout le code du parent dans l’enfant. Parfois juste une partie. L’héritage vous permet de réécrire certaines méthodes du parent dans l’enfant, c’est ce qu’on appelle l’overriding.

Quand vous faites :

class Parent(object):
 
    def truc(self):
        print 'foo'
 
 
class Enfant(Parent):
 
    pass

En fait vous faites en quelque sorte :

class Enfant(object):
 
    def truc(self):
 
        print 'foo'

Si vous faites :

class Enfant(object):
 
    def truc(self):
 
        print 'foo'
 
    def truc(self):
 
        print 'bar'

Vous allez écraser la première méthode avec la deuxième, car elles portent toutes les deux le même nom :

>>> Enfant().truc()
bar

“Ecraser” en anglais, se dit “to override”. Félicitation, vous venez d’apprendre l’overriding ! Ce qu’on vient de voir plus haut s’écrit comme ça dans le cadre de l’héritage :

class Parent(object):
 
    def truc(self):
        print 'foo'
 
class Enfant1(Parent): 
    pass # pas d'overriding
 
class Enfant2(Parent):
 
    def truc(self):
        print 'bar' # overriding !
 
 
>>> Enfant1().truc()
foo
>>> Enfant2().truc()
bar

Enfant1 a tout le code du parent qui est copié. Enfant2 aussi, mais il réécrit la méthode, donc sa version de la méthode écrase celle du parent.

Voyons ce que ça donne sur un cas plus concret dans la vie de tous les jours comme les prières chrétiennes (promis je fais les sourates du Coran si je fais un tuto Haskell) :

class Priere(object):
 
    def __init__(self, expiation=False):
        self.expiation = expiation
 
    def prier(self, nombre_de_fois=1):
 
        for x in xrange(nombre_de_fois):
            for ligne in self.texte:
                if self.expiation:
                    print ligne.upper()
                else:
                    print ligne
 
 
class PaterNoster(Priere):
 
    texte = ("Notre Père qui es aux cieux",
            "que ton nom soit sanctifié",
            "que ton règne vienne,",
            "que ta volonté soit faite",
            "sur la terre comme au ciel.",
            "Donne-nous aujourd’hui",
            "notre pain de ce jour,",
            "pardonne-nous nos offenses",
            "comme nous pardonnons aussi",
            "à ceux qui nous ont offensés",
            "et ne nous soumets pas à la tentation",
            "mais délivre-nous du mal.",
            "Amen")
 
 
# sur avemaria, on veut mettre en avant la version en araméen
# car l'araméen c'est trop cool (ça ressemble au langage des furlings
# dans stargate)
 
 
class AveMaria(Priere): 
 
    vf = ("Je vous salue, Marie pleine de grâce ;",
            "Le Seigneur est avec vous.",
            "Vous êtes bénie entre toutes les femmes",
            "Et Jésus, le fruit de vos entrailles, est béni.",
            "Sainte Marie, Mère de Dieu,",
            "Priez pour nous, pauvres pécheurs,",
            "Maintenant, et à l'heure de notre mort.",
            "Amen.")
 
    vo = ("ܡܠܝܬ ܛܝܒܘܬܐ",
          "ܡܪܢ ܥܡܟܝ",
          "ܡܒܪܟܬܐ ܐܢܬܝ ܒܢܫ̈ܐ",
          "ܘܡܒܪܟ ܗܘ ܦܐܪܐ ܕܒܟܪܣܟܝ ܡܪܢ ܝܫܘܥ",
          "ܐܘ ܩܕܝܫܬܐ ܡܪܝܡ ܝܠܕܬ ܐܠܗܐ",
          "ܨܠܝ ܚܠܦܝܢ ܚܛܝ̈ܐ",
          "ܗܫܐ ܘܒܫܥܬ ܘܡܘܬܢ",
          "ܐܡܝܢ܀")
 
 
    def prier(self, nombre_de_fois=1, version='vo'):
 
        for x in xrange(nombre_de_fois):
            for ligne in getattr(self, version, 'vo'):
                if self.expiation:
                    print ligne.upper()
                else:
                    print ligne
 
>>> AveMaria().prier()
ܡܠܝܬ ܛܝܒܘܬܐ
ܡܪܢ ܥܡܟܝ
ܡܒܪܟܬܐ ܐܢܬܝ ܒܢܫ̈ܐ
ܘܡܒܪܟ ܗܘ ܦܐܪܐ ܕܒܟܪܣܟܝ ܡܪܢ ܝܫܘܥ
ܐܘ ܩܕܝܫܬܐ ܡܪܝܡ ܝܠܕܬ ܐܠܗܐ
ܨܠܝ ܚܠܦܝܢ ܚܛܝ̈ܐ
ܗܫܐ ܘܒܫܥܬ ܘܡܘܬܢ
ܐܡܝܢ܀
>>> AveMaria().prier(2, 'vf')
Je vous salue, Marie pleine de grâce ;
Le Seigneur est avec vous.
Vous êtes bénie entre toutes les femmes
Et Jésus, le fruit de vos entrailles, est béni.
Sainte Marie,re de Dieu,
Priez pour nous, pauvres pécheurs,
Maintenant, et à l'heure de notre mort.
Amen.
Je vous salue, Marie pleine de grâce ;
Le Seigneur est avec vous.
Vous êtes bénie entre toutes les femmes
Et Jésus, le fruit de vos entrailles, est béni.
Sainte Marie, Mère de Dieu,
Priez pour nous, pauvres pécheurs,
Maintenant, et à l'heure de notre mort.
Amen.

Ici la classe AveMaria hérite de la classe Priere. Deux méthodes sont copiées de Priere vers AveMaria : __init__ et prier().

__init__ ne change pas. Donc AveMaria a toujours le __init__ de Priere. Par contre, on a overridé prier() dans AveMaria, qui est maintenant un code personnalisé.

Ceci nous permet donc de bénéficier d’une partie du code en commun (__init__), et de choisir un comportement différent pour d’autres bout du code (prier()).

La classe PaterNoster, elle, n’est pas affectée. Elle n’override rien, et sa méthode prier() est la même que celle de Priere.

Peut-être voulez-vous une exemple plus terre à terre (et moins dans les cieux) :

Prenez la bibliothèque path.py, par exemple. Normalement quand on additionne deux strings, ça les concatène. Mais quand on les divise, le comportement par défaut est de lever une erreur.

>>> '/home/sam' + '/blog'
'/home/sam/blog'
>>> '/home/sam' / '/blog'
Traceback (most recent call last):
  File "<ipython-input-58-8701a4e82b4b>", line 1, in <module>
    '/home/sam' / '/blog'
TypeError: unsupported operand type(s) for /: 'str' and 'str'

Path.py override ce comportement :

import os
 
class path(str): # hé oui, on peut hériter des types de base
 
    def __div__(self, other): # override le comportement face à l'opérateur '/'
 
        return path(os.path.join(self, other))
 
... p = path('/home/sam')
... print p / 'blog'
/home/sam/blog

Ca nous donne une très jolie interface pour la manipulation des chemins d’accès.

Polymorphisme et autres diableries

Comme d’hab en prog, on adore les mots qui font super hype de la vibes du flex.

Le polymorphisme fait partie de ces termes compliqués qui cachent une notion simple : avoir une API commune qui fait des choses différentes.

Voyez-vous, nos deux classes AveMaria et PaterNoster, sont toutes les deux filles de la même classe parente. Elles se ressemblent donc beaucoup : elles ont des attributs et des méthodes en commun :

>>> p1 = AveMaria()
>>> p2 = PaterNoster()
>>> p1.expiation
False
>>> p2.expiation
False
>>> p1.prier
<bound method AveMaria.prier of <__main__.AveMaria object at 0x20618d0>>
>>> p2.prier
<bound method PaterNoster.prier of <__main__.PaterNoster object at 0x2061c90>>
>>> p1.__init__
<bound method AveMaria.__init__ of <__main__.AveMaria object at 0x20618d0>>
>>> p2.__init__
<bound method PaterNoster.__init__ of <__main__.PaterNoster object at 0x2061c90>>

Ce sont pourtant des classes différentes (et prier() ne fait pas du tout le même chose), mais elles partagent ce qu’on appelle une API (une interface), c’est à dire une manière de les utiliser.

Cette capacité à être utilisé pareil, mais produire un résultat diffférent est ce qu’on appelle le polymorphisme (et c’est la base du duck typing). Concrètement ça veut dire que vous pouvez utiliser les deux classes dans le même contexte, sans vous soucier de si c’est l’une ou l’autre :

>>> prieres = [AveMaria(), AveMaria(), PaterNoster(), AveMaria(), PaterNoster()]
>>> prieres
[<__main__.AveMaria object at 0x2061d50>, <__main__.AveMaria object at 0x2061d90>, <__main__.PaterNoster object at 0x2061f50>, <__main__.AveMaria object at 0x2061f90>, <__main__.PaterNoster object at 0x2061fd0>]
>>> for priere in prieres: 
...     priere.prier() # prier une prière, ça se fait pareil
 
ܡܠܝܬ ܛܝܒܘܬܐ
ܡܪܢ ܥܡܟܝ
ܡܒܪܟܬܐ ܐܢܬܝ ܒܢܫ̈ܐ
ܘܡܒܪܟ ܗܘ ܦܐܪܐ ܕܒܟܪܣܟܝ ܡܪܢ ܝܫܘܥ
ܐܘ ܩܕܝܫܬܐ ܡܪܝܡ ܝܠܕܬ ܐܠܗܐ
ܨܠܝ ܚܠܦܝܢ ܚܛܝ̈ܐ
ܗܫܐ ܘܒܫܥܬ ܘܡܘܬܢ
ܐܡܝܢ܀
ܡܠܝܬ ܛܝܒܘܬܐ
ܡܪܢ ܥܡܟܝ
ܡܒܪܟܬܐ ܐܢܬܝ ܒܢܫ̈ܐ
ܘܡܒܪܟ ܗܘ ܦܐܪܐ ܕܒܟܪܣܟܝ ܡܪܢ ܝܫܘܥ
ܐܘ ܩܕܝܫܬܐ ܡܪܝܡ ܝܠܕܬ ܐܠܗܐ
ܨܠܝ ܚܠܦܝܢ ܚܛܝ̈ܐ
ܗܫܐ ܘܒܫܥܬ ܘܡܘܬܢ
ܐܡܝܢ܀
Notre Père qui es aux cieux
que ton nom soit sanctifié
que ton règne vienne,
que ta volonté soit faite
sur la terre comme au ciel.
Donne-nous aujourd’hui
notre pain de ce jour,
pardonne-nous nos offenses
comme nous pardonnons aussi
à ceux qui nous ont offensés
et ne nous soumets pas à la tentation
mais délivre-nous du mal.
Amen
ܡܠܝܬ ܛܝܒܘܬܐ
ܡܪܢ ܥܡܟܝ
ܡܒܪܟܬܐ ܐܢܬܝ ܒܢܫ̈ܐ
ܘܡܒܪܟ ܗܘ ܦܐܪܐ ܕܒܟܪܣܟܝ ܡܪܢ ܝܫܘܥ
ܐܘ ܩܕܝܫܬܐ ܡܪܝܡ ܝܠܕܬ ܐܠܗܐ
ܨܠܝ ܚܠܦܝܢ ܚܛܝ̈ܐ
ܗܫܐ ܘܒܫܥܬ ܘܡܘܬܢ
ܐܡܝܢ܀
Notre Père qui es aux cieux
que ton nom soit sanctifié
que ton règne vienne,
que ta volonté soit faite
sur la terre comme au ciel.
Donne-nous aujourd’hui
notre pain de ce jour,
pardonne-nous nos offenses
comme nous pardonnons aussi
à ceux qui nous ont offensés
et ne nous soumets pas à la tentation
mais délivre-nous du mal.
Amen

Le polymorphisme, c’est donc l’utilisation de l’héritage pour faire des choses différentes, mais en proposant la même interface (ensemble de méthodes et d’attributs) pour le faire. Le polymorphisme s’étend aussi à la réaction aux opérateurs (+, -, /, or, and, etc), d’autant qu’en Python, c’est implémenté avec les méthodes nommées avec __.

Un dernier point pour les gens qui se demandent ce que veut dire overloader. L’overload est lié à l’override et au polymorphisme, mais ce n’est pas la même chose : elle consiste à overrider plusieurs fois une même méthode avec une signature différente. Il n’y a pas d’overload en Python, la notion ne nous concerne donc pas. Si vous voulez en apprendre plus, chopez un tuto Java ou C++.

Qui est qui

Avec l’héritage vient la notion de type. Quand vous créez une classe, vous créez un nouveau type. Quand vous sous-classez, vous créez un sous-type.

C’est intéressant, car une classe fille, est de son propre type ET du type de son parent (mais l’inverse n’est pas vrai).

Avec Python, on vérifie cela avec isinstance() :

>>> isinstance(Priere(), Priere) # instance de sa propre classe
True
>>> isinstance(Priere(), str) # pas l'instance d'une classe quelconque
False
>>> isinstance(Priere(), AveMaria) # pas l'instance d'un enfant
False
>>> isinstance(AveMaria(), AveMaria) # instance de sa propre classe
True
>>> isinstance(AveMaria(), Priere) # instance de sa classe parente
True

C’est utile, car certains comportements sont basés sur le type. Le plus important étant le mécanisme des exceptions. Un try / except arrêtera l’exception demandée, ou du même type.

class MonExceptionPerso(Exception):
    pass
 
 
class FillesDeMonExceptionPerso(MonExceptionPerso):
    pass
 
 
try:
    raise MonExceptionPerso('Alerte ! Alerte !')
except MonExceptionPerso:
    print 'Exception arrêtée'
 
 
try:
    raise FillesDeMonExceptionPerso('Alerte ! Alerte !')
except FillesDeMonExceptionPerso:
    print 'Exception arrêtée'
 
 
try:
    raise FillesDeMonExceptionPerso('Alerte ! Alerte !')
except MonExceptionPerso:
    print 'Exception arrêtée'    
 
 
 
try:
    raise MonExceptionPerso('Alerte ! Alerte !')
except FillesDeMonExceptionPerso:
    print 'Exception arrêtée'    
 
 
Exception arrêtée
Exception arrêtée
Exception arrêtée
Traceback (most recent call last):
  File "<ipython-input-67-7e7aec1e78c9>", line 21, in <module>
    raise MonExceptionPerso('Alerte ! Alerte !')
MonExceptionPerso: Alerte ! Alerte !

MonExceptionPerso est de type MonExceptionPerso, donc l’exception est arrêtée. FillesDeMonExceptionPerso est de type FillesDeMonExceptionPerso, donc l’exception est arrêtée. FillesDeMonExceptionPerso, qui hérite de MonExceptionPerso, et donc de type MonExceptionPerso est arrêtée.

En revanche, MonExceptionPerso n’est PAS de type FillesDeMonExceptionPerso. Donc elle n’est pas arrêtée.

Je le signale car il est très courant de faire des enfants de ValueError, IOError, IndexError, KeyError, etc. et de le lever dans son propre programme. Cela permet à l’utilisateur de son code de pouvoir attraper soit toutes les erreurs de son code en faisant un except sur l’enfant, soit attraper toutes les erreurs de type ValueError, IOError, IndexError, KeyError en faisant le except sur le parent. On laisse ainsi une marge de manoeuvre dans la gestion des erreurs.

Appeler la méthode de la classe parent

Bon, vous avez overridé une méthode du parent. Mais c’est une groooooooooooooossse méthode. Vous allez pas la réécrire en entier, si ?

class Escorte(object):
 
    def calculer_prix(self, heures, tarif):
 
        return heures * tarif
 
 
class EscorteDeLuxe(Escorte):
 
    def calculer_prix(self, heures, tarif, supplement):
 
        tarif =  heures * tarif
 
        return tarif + (tarif * supplement / 100)

Sur cet exemple éminement intellectuel, calculer_prix() est simple, donc tout réécrire dans EscorteDeLuxe n’est pas grave. Mais si c’était une Geisha, hein ? Avec un manuel en Japonais ?

Pour éviter ces problèmes de complexité liés à la globalisation, le perméabilisation des frontières et les sites de streaming, on peut appeler la méthode du parent, dans l’enfant, en utilisant super():

class EscorteDeLuxe(Escorte):
 
    def calculer_prix(self, heures, tarif, supplement):
 
        # ceci est une manière compliquée de faire Escorte.calculer_prix
        # et de récupérer le résultat
        tarif =  super(EscorteDeLuxe, self).calculer_prix(heures, tarif)
 
        return tarif + (tarif * supplement / 100)

C’est exactement la même chose que plus haut. Sauf qu’au lieu de copier / coller le code du parent, on l’appelle directement.

Je résume :

  • on hérite du parent, et de son code
  • on override son code, car on veut un comportement différent
  • mais on veut quand même une partie du comportement du parent
  • donc dans la méthode de l’enfant, on appelle la méthode du parent

Je réformule : quand vous overridez la méthode du parent dans l’enfant, celle du parent ne disparait pas. Elle est remplacée uniquement dans l’enfant. Et vous pouvez toujours vous servir de la version du parent en utilisant super(ClassEncours, self).nom_de_method(arguments) si vous en avez besoin.

Bon, c’était un gros morceau, et je suis pas sûr de pas être allé trop fort. Donc laissez en comment les remarques sur ce qui pourrait être mieux expliqué. La prochaine fois, on verra des usages poussés comme la composition, la délégation et tous ces trucs d’un vrai code de production.

21 thoughts on “Le guide ultime et définitif sur la programmation orientée objet en Python à l’usage des débutants qui sont rassurés par les textes détaillés qui prennent le temps de tout expliquer. Partie 4.

  • Sam Post author

    On en est qu’à la partie 4. Je pense qu’il y aura entre 6 et 8 parties.

  • roro

    Rien à dire. C’est du cousu main et repassé à la pâtemouille.
    A part qu’on hallucine un peu sur l’exemple…Mais bon, c’est Dimanche,les cloches,toussa…Normal.
    Tu m’a l’air d’être en grande forme.
    je me barre avant l’arrivée des abeilles…ça va pas tarder à bourdonner sévère par ici.

  • Cricri

    Si je ne m’abuse, l’héritage de EscorteDeLuxe n’est pas correcte.

    Excellente série pour le débutant, depuis le temps que j’en ai pas fait en python, ça me rafraichit la mémoire ;)

  • roro

    @Sam:
    Il serait bon que lorsque toutes les corrections de tes ignobleries seront faites, tu mette un tampon “Valided”.
    ça m’éviterais de recharger la page cinq fois, et de cinq fois en retirer les com’s, les images, et un tas de trucs chelous dont est copieusement garnis le html.
    Pour n’en garder que la substantifique moelle, dans sa merveilleuse mise en page.

  • Sam Post author

    Le truc c’est que c’est un travail en perpétuelle continuation. Parfois un article a encore des corrections apportées par les lecteurs des jours après la publication. Je n’ai aucun moyen de savoir à l’avance si j’ai atteint la perfection, et je n’ai pas envie de passée des heures et des heures à checker tous les articles.

    C’est aussi à ça que servent les coms.

    Un article comme celui-ci me prend entre 2 et 4 heures à écrire. C’est normal qu’il y ai des coquilles. Je compte fortement sur l’intelligence collective pour soulager ma tâche de relecture.

  • JEEK

    Supposons que vous fassiez un jeu vidéo pour apprendre les prières chrétiennes, et vous que ayez une classe par prière :

    Supposons que vous fassiez un jeu vidéo pour apprendre les prières chrétiennes, et que vous ayez une classe par prière :

    Hop… ;-)

    Prenez la bibliothqèe path.py, par exemple.

    Prenez la bibliothèque path.py, par exemple.

    Re’hop… ;-)

    La qualité est toujours là ; ça m’a rappelé mon stage de formation prog en Java (où j’ai découvert les joies de la POO en même temps) mais en nettement moins douloureux…

    Merci Sam & Max

  • roro

    @Sam:
    Je te comprends. je reviendrai sur les anciens faire des “compare”, ça va aller.
    Repose toi, on en a pour un moment à digérer…

  • kontre

    @roro Tu nettoie le html à la main ? Tu n’as pas essayé de créer un script pour ne récupérer que la substantifique moelle ? Y’a des modules python pour parser le html (il vaut mieux éviter de le faire à la main).

  • kontre

    Et sinon, il n’y a pas de souci à ne pas mettre les strings en unicode ? Même en araméen ?

    Je viens de tester, le .upper() marche pour les lettres accentuées en unicode, mais pas en ascii (d’où vos exemples tout moches). Et apparemment il n’y a pas de majuscules en araméen…

  • Sam Post author

    Il y a toujours des problèmes à ne pas utiliser l’unicode. Mais mélanger trop de choses dans le tuto ne va pas aider à sa compréhension. Pour l’exemple, c’est suffisant.

  • roro

    @kontre:
    J’aime bien voir ce que je fais, et avec l’habitude, c’est très rapide. Après je mets tout dans un dossier, j’ajoute des liens menant directement à des parties de sujet. Ce qui me permets de voir différents emplois d’une même chose, et le “retour arrière” me renvoie d’où je viens, plus pratique que des ouvertures de fichiers + recherche dans le fichier.
    De l’hybridation python/html, en quelque sôôrte.

  • Mikko

    Your site? 0bin.net has error.
    The #hash contains charachter = which breaks almost all forum hyperlinks. please fix if you can and TY

    • Sam Post author

      The problem doesn’t come from 0bin. If a forum can’t parse the URL, their URL parser is broken. Still, you can use the “get short url” feature from 0bin which doesn’t contain any “=” sign.

  • Réchèr

    Ding !! C’est encore moi !

    une truc compliqué
    un truc compliqué

    Petite appartée sur object
    Petit aparté sur object

    notre père qui es au cieux
    On croit que y’a une faute, j’ai vérifié, y’en a pas. le mot “es” est bien sans T.

    par l’araméen c’est trop cool
    car l’araméen c’est trop cool

    for ligne in getattr(self, version, 'vo'):

    Le paramètre ‘default’ du getattr ne fonctionne pas comme ça.
    Il faut y mettre directement la valeur, qui sera renvoyée telle quelle si l’attribut n’existe pas. On ne peut pas mettre une chaîne de caractère correspondant à un nom d’attribut censé exister.
    Faudrait donc remplacer par ça :

    for ligne in getattr(self, version, AveMaria.vo):

    http://docs.python.org/2/library/functions.html#getattr

    ��ܫܐ ܘܒܫܥܬ ܘܡܘܬܢ
    ܗܫܐ ܘܒܫܥܬ ܘܡܘܬܢ
    Je vous laisse retrouver ou c’est. (Bidouille avec le caractère magique de l’unicode qui indique que le texte est de droite à gauche ?)

    c’est implémenté avec les méthodes nommées avec __.
    c’est implémenté par les méthodes nommées avec __.
    Parce que sinon ça fait deux fois “avec”.

    FillesDeMonExceptionPerso, qui hérite de MonExceptionPerso, et donc de type MonExceptionPerso est arrêtée.
    FillesDeMonExceptionPerso, qui hérite de MonExceptionPerso, est donc de type MonExceptionPerso, elle est donc arrêtée.

    de le lever dans son propre programme.
    de les lever dans son propre programme.

    manoeuvre
    manœuvre
    (Ça, pour mettre de l’araméen, y’a du monde. Mais pour faire un “œ”, y’a plus personne. Avec un clavier bépo, ce genre de subtilité ne pose aucun problème)

         def calculer_prix(self, heures, tarif, supplement):
            tarif =  heures * tarif
            return tarif + (tarif * supplement / 100)

    Utilisation un peu trop abusive de la variable “tarif”. Ça fait un risque de confusion entre le tarif horaire (la valeur passée en paramètre), et le tarif de la prestation (la valeur renvoyée par la fonction). Je propose d’utiliser “prix” à la place. Ce sera d’autant plus cohérent que la fonction s’appelle “calculer_prix”.
    Donc :

         def calculer_prix(self, heures, tarif, supplement):
            <strong>prix </strong>=  heures * tarif
            return <strong>prix </strong>+ (<strong>prix </strong>* supplement / 100)

    Et du coup, le bloc de code qui vient après serait comme ça :
    class EscorteDeLuxe(Escorte):

        def calculer_prix(self, heures, tarif, supplement):
     
            # ceci est une manière compliquée de faire Escorte.calculer_prix
            # et de récupérer le résultat
            <strong>prix </strong>=  super(EscorteDeLuxe, self).calculer_prix(heures, tarif)
            return <strong>prix </strong>+ (<strong>prix </strong>* supplement / 100)

    Mais ceci reste à l’appréciation du rédacteur. Utiliser le même nom de variable, ça se défend aussi.

  • Sam Post author

    Merci beaucoup Recher. Tu veux pas que je te donne directement un nouveau compte avec accès au backend plutôt ? Comme ça tu as pas à tout réécrire toi-même dans un poste, ça double ton taff pour rien là.

  • Réchèr

    Merci beaucoup, mais c’est peut être pas la peine. En fait j’ai juste prévu de relire et corriger les articles sur la POO, parce que c’est un bazar qui me semble important. Pour le reste, je laisse les correcteurs dotés de plus d’abnégation s’en occuper.

  • sensini42

    Encore moi :]

    c’est à dire : c’est-à-dire

    je l’ai fais : fait
    A partir : À partir

    (D’ailleurs à partir de Python 3, elles sont activées par défaut): (D’ailleurs à partir de Python 3, elles sont activées par défaut.) (point dans la parenthèse

    notre classe Priere : balise code manquante

    Deux méthodes sont copiées de Priere vers AveMaria : __init__ et prier(). : pourquoi des parenthèses à prier et pas à __init__ ? (idem lignes suivantes)

    d’autres bout de code : bouts

    une exemple : un exemple

    Prenez la bibliothèque path.py : prenez (pas de majuscule après «:» pas de capitale non plus :þ)

    Path.py override ce comportement : … p = path à changer en »»

    de la vibes du flex : j’utilise vibe mais ai entendu vibes. À voir.

    le même chose : la
    c’est à dire : c’est-à-dire
    à être utilisé : a être

    sans vous soucier de si c’est l’une ou l’autre : soit plus de ponctuation, soit «soucier de savoir si»

    etc etc.

    manoeuvre : manœuvre non, c’est bon…

    le perméabilisation : la

    énumération sans ;

Comments are closed.

Des questions Python sans rapport avec l'article ? Posez-les sur IndexError.