Terminale - Programmation Orientée Objet

Introduction

La Programmation Orientée Objet (souvent abrégé POO) est un paradigme de programmation (une façon de programmer) que vous utilisez sans le savoir à chaque fois que vous écrivez un programme en Python.

En effet, en Python, tout est objet: listes, dictionnaires, chaînes de caractères... et même les entiers !

Un objet est une structure de données que l'on manipule grâce à des méthodes qui lui sont propres. Par exemple, pour ajouter un élément dans une liste, on utilise la méthode append. Avec la méthode pop, on retire et on récupère le dernier élément :

>>> liste = [1,2,3,4]
>>> liste.append(5)
>>> liste
[1,2,3,4,5]
>>> liste.pop()
5
>>> liste
[1,2,3,4]

Dans le code ci-dessus, liste est un objet. Les méthodes append et pop réalisent pour nous les modifications nécessaires en ajoutant et supprimant des éléments dans la liste. Ces opérations internes sont invisibles pour nous... et tant mieux. C'est d'ailleurs ce qui fait la simplicité de Python, notamment sur la gestion de la mémoire.

En ce sens, un objet est semblable à une boîte noire composée d'une structure interne complexe mais dont la compréhension du fonctionnement n'est pas forcément nécessaire au programmeur, qui se contente d’utiliser les méthodes disponibles pour cet objet.


Une photocopieuse est un objet : elle possède une méthode copier permettant de photocopier des documents sans avoir conscience de son fonctionnement interne.

Jusqu'à maintenant, nous nous sommes contentés de créer et de manipuler des objets comme des listes, des dictionnaires. Il est temps d'apprendre à concevoir des objets.

Conception d'un objet

Objets et Classes

Pour concevoir un objet, il faut écrire la classe correspondante. La classe est en quelque sorte le moule qui va servir à fabriquer notre objet, c'est un plan de construction, un modèle. Cependant, les objets créées avec une même classe ne sont pas identiques. Ils présentent des caractéristiques et des fonctions communes mais peuvent différer sur certains points.


Trois objets issus d'une même classe

Pour définir une classe en Python, on utilise le mot-clé class suivi du nom de la classe.

class NomDeLaClasse:
    # Implémentation de la classe
Le nom d'une classe respecte la convention CapWords : il est formé d’une suite de mots commençant tous par une majuscule, sans caractère de séparation (ni espace, ni tiret). Ce n'est pas une obligation mais une convention.

Attributs et Méthodes

Imaginons la situation suivante : vous développez un RPG et on vous demande d'implémenter une classe Personnage. Par où commencer ? Eh bien par se poser les deux questions suivantes :


Un exemple pour la classe Personnage

Commençons par définir quelques attributs. Pour notre classe Personnage, on peut considérer qu'un personnage possède un nom, un certain nombre de points de vie, ainsi qu'une arme qui inflige des dégâts plus ou moins importants.

class Personnage:
    """ Définition d'une classe Personnage
    Un personnage est défini par :
    - son nom
    - son nombre de points de vie
    - le nom de son arme
    - les points de dégats de son arme """
    
    """ Constructeur de la classe Personnage """
    def __init__(self, nom, pv, nom_arme, degats_arme):
        self.nom = nom
        self.pv = pv
        self.nom_arme = nom_arme
        self.degats_arme = degats_arme

Quelques précisions

Il ne nous reste plus qu'à créer des objets de type Personnage:

class Personnage:
    """ Définition d'une classe Personnage
    Un personnage est défini par :
    - son nom
    - son nombre de points de vie
    - le nom de son arme
    - les points de dégats de son arme """
    
    """ Constructeur de la classe Personnage """
    def __init__(self, nom, pv, nom_arme, degats_arme):
        self.nom = nom
        self.pv = pv
        self.nom_arme = nom_arme
        self.degats_arme = degats_arme

frodon = Personnage("Frodon Sacquet", 150, "Dard", 20)
gollum = Personnage("Gollum", 100, "Poings", 10)    

Dans l'exemple ci-dessus, on crée deux objets (appelés instances de classe), chacun ayant ses propres caractéristiques. On peut accéder aux attributs de nos objets en écrivant objet.attribut :

>>> frodon.nom
'Frodon Sacquet'
>>> frodon.pv
150
>>> gollum.nom_arme
'Poings'

Bien entendu, un objet ne se limite pas à une liste d'attributs. Tout l'intérêt de la POO réside dans la définition de méthodes propres à nos objets. Une méthode est une fonction qui va agir directement sur notre objet.

Nos personnages peuvent par exemple se soigner en buvant une potion, ou changer l'arme qu'ils portent.
On définit ces fonctions dans la classe Personnage, en spécifiant toujours le paramètre self en premier :

class Personnage:
    """ Définition d'une classe Personnage
    Un personnage est défini par :
    - son nom
    - son nombre de points de vie
    - le nom de son arme
    - les points de dégats de son arme """
    
    """ Constructeur de la classe Personnage """
    def __init__(self, nom, pv, nom_arme, degats_arme):
        self.nom = nom
        self.pv = pv
        self.nom_arme = nom_arme
        self.degats_arme = degats_arme
    
    """ Augmente le nombre de points de vie du personnage d'une quantité donnée """
    def boire_potion(self, quantite):
        self.pv += quantite
        print(f"{self.nom} récupère {quantite} points de vie.")
    
    """ Modifie le nom et les dégâts de l'arme portée par le personnage """
    def changer_arme(self, nom_nouvelle_arme, degats_nouvelle_arme):
        self.nom_arme = nom_nouvelle_arme
        self.degats_arme = degats_nouvelle_arme
        print(f"{self.nom} change d'arme pour : {self.nom_arme}")

frodon = Personnage("Frodon Sacquet", 150, "Dard", 20)
gollum = Personnage("Gollum", 100, "Poings", 10)    
Les méthodes boire_potion et changer_arme prennent un paramètre self qu'il n'est pas nécessaire de préciser lorsqu'on appelle ces fonctions. Lorsque l'on écrit gollum.boire_potion(20), le paramètre self désigne alors l'objet gollum, et tout se déroule comme si l'on écrivait boire_potion(gollum, 20).
>>> gollum.boire_potion(20)
Gollum récupère 20 points de vie
>>> gollum.pv
120
>>> frodon.changer_arme("Excalibur", 300)
Frodon change d’arme pour : Excalibur
>>> frodon.nom_arme
'Excalibur'
>>> frodon.degats_arme
300

À l'attaque !

Il est temps de faire combattre nos personnages en implémentant une méthode attaquer dans la classe Personnage. Cette fonction prend un seul paramètre (en plus de self) : le personnage à attaquer (qui est un objet de type Personnage). Son fonctionnement est simple : lorsqu'un personnage attaque un autre personnage, on lui retire autant de points de vie que les dégâts faits par l'arme de notre personnage attaquant.

class Personnage:
    # Seule la méthode attaquer est représentée

    """ Attaque d'un autre personnage : on diminue ses PV """
    def attaquer(self, adversaire):
        adversaire.pv -= self.degats_arme
        print(f"{adversaire.nom} reçoit {self.degats_arme} points de dégâts.")
>>> frodon.attaquer(gollum)
Gollum reçoit 20 points de dégâts.
>>> gollum.attaquer(frodon)
Frodon reçoit 10 points de dégâts.

Codée ainsi, la méthode attaquer fonctionne, mais il est préférable de créer une seconde méthode recevoir_degats qui se chargera de soustraire les dégâts au personnage attaqué, d'afficher une phrase mais aussi vérifier que celui-ci ne meure pas...

class Personnage:
    # Seule les méthodes attaquer et recevoir_degats sont représentées

    """ Attaque d'un autre personnage : l'adversaire reçoit des dégâts """
    def attaquer(self, adversaire):
        adversaire.recevoir_degats(self.degats_arme)

    """ Réception des dégâts """
    def recevoir_degats(self, degats):
        self.pv -= degats
        print(f"{self.nom} reçoit {degats} points de dégâts.")
Il est d'usage de ne pas modifier les attributs d'un objet à l'extérieur d'une classe, autre qu'avec self (comme en écrivant adversaire.pv), ni même d'accéder directement à leur valeur : c'est le principe d'encapsulation, principe sacré en POO. Facultatif en Python, il est cependant obligatoire dans d'autres langages de programmation. Pour en savoir plus, on pourra se reporter à la section Encapsulation de la leçon suivante : "Programmation Orientée Objet - Compléments"
Compléter la fonction recevoir_degats afin qu'une phrase du type "Gollum est mort..." s'affiche si les points de vie du personnage sont inférieurs à 0 après la réception des dégâts.
Effectuer un combat épique entre nos deux personnages. Ne pas hésiter à introduire d'autres personnages, ni à implémenter d'autres méthodes comme lancer_sort...