Skip to content

Programmation Orientée Objet du point de vue du langage Python

Introduction

La POO est bien plus qu'un simple paradigme de programmation : il s'agit surtout d'une méthode de conception centrée sur les objets et qui possède bien des avantages. Voici ce qu'en dit Gérard Swinnen dans son livre Apprendre à programmer avec Python 3 :

Les classes sont les principaux outils de la programmation orientée objet (Object Oriented Programming ou OOP). Ce type de programmation permet de structurer les logiciels complexes en les organisant comme des ensembles d’objets qui interagissent, entre eux et avec le monde extérieur.

Le premier bénéfice de cette approche de la programmation réside dans le fait que les différents objets utilisés peuvent être construits indépendamment les uns des autres (par exemple par des programmeurs différents) sans qu’il n’y ait de risque d’interférence. Ce résultat est obtenu grâce au concept d’encapsulation : la fonctionnalité interne de l’objet et les variables qu’il utilise pour effectuer son travail, sont en quelque sorte « enfermées » dans l’objet. Les autres objets et le monde extérieur ne peuvent y avoir accès qu’à travers des procédures bien définies : l’interface de l’objet.

En particulier, l’utilisation de classes dans vos programmes va vous permettre – entre autres avantages – d’éviter au maximum l’emploi de variables globales. Vous devez savoir en effet que l’utilisation de variables globales comporte des risques, d’autant plus importants que les programmes sont volumineux, parce qu’il est toujours possible que de telles variables soient modifiées, ou même redéfinies, n’importe où dans le corps du programme (ce risque s’aggrave particulièrement si plusieurs programmeurs différents travaillent sur un même logiciel).

Même si Python n'est pas considéré comme le meilleur langage orienté objet, de l'avis de Y. Roggeman, Python fait partie des :

[...] langages les plus populaires (utilisés) qui ont été conçus dès l'origine pour offrir, sinon toutes, au moins les principales caractéristiques constitutives d'un langage orienté objet.

La POO basique

Python est un langage à classes. Voici le vocabulaire associé à ce types de langages :

  • la classe est un type, un ensemble d'objets partageant les mêmes propriétés concrétisées par une liste de membres ;
  • l'objet désigne une instance de la classe ;
  • les membres d'une classe (et de ses objets) sont soit des attributs codifiant l'état particulier d'un objet, sa valeur, soit des méthodes agissant sur l'objet.

Voyons la mise en oeuvre.

Première class, premier objet

En Python, la définition d'une classe se fait par le mot-clé class suivi du nom de la classe, comme ceci :

class Bidon:
    """Une classe qui ne fait pas grand chose"""
    pass

Et la création d'une instance se fait par appel à la classe, comme s'il s'agissait d'une fonction, comme ceci :

rien = Bidon()

Cette fonction est le constructeur de la classe. Et nous pouvons vérifier que rien est de type Bidon :

>>> type(rien)
__main__.Bidon

>>> print(rien)
<__main__.Bidon object at 0x0000025485C9B190>

Mais un objet bidon n'est pas très utile. Voyons comment créer un objet Personnage (celui qui pourrait combattre dans un jeu vidéo) possédant des caractéristiques et pouvant effectuer des actions.

Commençons par créer une nouvelle classe :

class Personnage:
    """Modélise un personnage du jeu Combat Pour de Faux"""

Attributs d'instance, constructeur et initialiseur

Lors de la création d'un objet (informatique au sens POO), se pose la question de ce que nous voulons (devons) modéliser de notre objet (au sens du monde réel, du problème) dans le contexte du problème à résoudre.

Notre personnage de jeu va avoir un nom, probablement des points de vie (PV en abrégé). Et pour l'instant ce sera tout : notre jeu est très basique. Les PV pouvant varier, il nous faudra mémoriser deux valeurs : les PV maximum et les PV actuels. En résumé, nous avons trois caractéristiques à modéliser pour notre personnage de jeu :

  • nom
  • points de vie maximum
  • points de vie actuels

Ces caractéristiques se traduisent par des attributs d'instance c'est-à-dire des attributs que chaque instance de la classe Personnage aura avec des valeurs propres. Créons un premier personnage (par appel au contructeur, comme vu précédemment) et donnons lui les trois attributs cités :

>>> merlin = Personnage()
>>> merlin.nom = 'Merlin'
>>> merlin.pv_max = 80
>>> merlin.pv = 80

Constatons que la création d'un attribut nom pour merlin se fait :

  • par simple affectation, comme pour une variable ; on parle d'ailleurs aussi de variables d'instance ;
  • par la notation pointée déjà rencontrée : par exemple lorsque nous ajoutons un élément à un objet list (ma_liste.append(1)).

Toutefois, il faut reconnaître que cette création en deux étapes n'est guère commode et il serait plus simple (et surtout beaucoup plus dans l'esprit de la programmation objet) de pouvoir créer les attributs avec leurs valeurs respectives à la création de l'instance. Ce sera le rôle de l'initialiseur qui est une fonction définie dans la classe (on parle de méthode) portant en Python le nom très particulier __init__ : double underscore (le caractère souligné _) suivi de init puis de nouveau le double underscore.

Cette fonction accepte des paramètres qui seront les valeurs à donner aux attributs à la création de l'objet ; le premier paramètre est particulier : ce sera toujours le mot-clé self.

Voici une nouvelle définition de Personnage :

class Personnage:
    """Modélise un personnage du jeu Combat Pour de Faux"""

    def __init__(self, nom, pv_max):
        self.nom = nom
        self.pv_max = pv_max
        self.pv = pv_max

La création de personnages est plus aisée :

>>> merlin = Personnage('Merlin', 80)
>>> arthur = Personnage('Arthur', 120)

self

Ce petit mot-clé perturbe pas mal les débutants en général. Le temps de s'habituer, il faut simplement se rappeler qu'il sera toujours le premier paramètre des méthodes d'instance et qu'il viendra préfixer chaque appel à un attribut ou une méthode d'instance.

Notons que l'appel à la mode objet :

ma_liste.append(element)

peut se faire à la mode fonction impérative :

list.append(ma_liste, element)

ce qui éclaire sur le premier paramètre de la fonction list.append.

Méthodes d'instance

Une méthode, c'est ce qui permet à une instance de réaliser des actions. Techniquement ce sont des fonctions définies dans le corps de la classe, et qui prennent toujours au moins un premier paramètre qui est self.

Certaines méthodes sont particulières, leurs noms commencent par un double undescore (le caractère _). On a vu une première de ces méthodes : l'initialiseur __init__.

Définissons notre deuxième méthode pour la classe Personnage : ko qui retourne True si et seulement si les points de vie de notre personnage sont à 0.

class Personnage:
    """Modélise un personnage du jeu Combat Pour de Faux"""

    def __init__(self, nom, pv_max):
        self.nom = nom
        self.pv_max = pv_max
        self.pv = pv_max

    def ko(self):
        return self.pv == 0

Utilisation :

>>> merlin = Personnage('Merlin', 80)
>>> merlin.pv -= 30
>>> merlin.ko()
False
>>> merlin.pv -= 50
>>> merlin.ko()
True

Certaines méthodes permettent de modifier des valeurs d'attributs. Voici un exemple :

def soigner(self, qte_soin):
    self.pv += qte_soin

La méthode soigner modifie la valeur de la variable d'instance pv en y ajoutant un montant. Nous verrons dans le prochain chapitre comment la POO encourage à n'exposer que des méthodes vers l'extérieur (on parle d'interface) en masquant les attributs. Il s'agit d'un des quatre éléments constitutifs de l'orienté objet (nous verrons cela dans la section suivante) : l'encapsulation.