Aller au contenu

Programmation Orientée Objet

Classes, instances et attributs

L'objectif d'une classe est de pouvoir créer et manipuler des objets qui ont des caractéristiques communes. Une classe définit donc un "patron" que l'on utilisera par la suite pour créer des objets similaires : on parle d'instances de la classe. Ces objets ont en commun :

  • des informations qui les décrivent, on parle d'attributs ;
  • des opérations qui permettent de les manipuler, on parle de méthodes.

Pour définir une nouvelle classe, on utilise le mot-clé class, suivit du nom de la classe (avec une majuscule, par convention), suivit de deux points. Le corps de la classe est constitué de tout le code qui se trouve indenté par rapport à cette première ligne, jusqu'à la première ligne non indentée. Une méthode de la classe est une fonction définie dans le corps de la classe.

La méthode spéciale __init__ est appelée lors de l'instanciation d'un nouvel objet. On l'appelle le constructeur de la classe.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
class Poisson:
    """ Représente un poisson """
    def __init__(self, n, t):
        """ Poisson, str, int, bool -> None """
        self.nom = n
        self.poids = t
        self.faim = True

nemo = Poisson("Nemo", 15)
dory = Poisson("Dory", 12)

À retenir. Si le constructeur d'une classe C est du type __init__(self, arg1, arg2), on instancie une nouvelle variable de la classe C à l'aide de l'instruction C(arg1, arg2).

1
2
print(nemo)
print(dory)
<__main__.Poisson object at 0x7d1dcc849520>
<__main__.Poisson object at 0x7d1dcc84bdd0>

img

À retenir. Si objet est une instance de la classe C et possède un attribut de nom nom_attr, alors il est possible d'accéder et de modifier cet attribut avec la syntaxe objet.nom_attr (on appelle ceci la notation pointée).

1
2
3
4
print(nemo.nom)
print(nemo.faim)
nemo.faim = True
print(nemo.faim)
Nemo
True
True

Écrire une fonction nourrir qui prend en argument un objet poisson de type Poisson et un entier repas et simule l'effet d'un repas sur le poisson :

  • si le poisson a faim, alors le poids du poisson augmente de repas et le poisson n'a plus faim ;
  • si le poisson n'a pas faim, on ne fait rien.

La fonction renverra True si le poisson a été nourri, False sinon.

1
2
3
4
5
6
7
def nourrir(poisson, repas):
    """ Poisson, int -> None """
    if poisson.faim:
        poisson.poids = poisson.poids + repas
        poisson.faim = False
        return True
    return False

Méthodes et interface

Une méthode d'une classe est une fonction appartenant au corps de cette classe. Son premier paramètre est toujours un objet de la classe . Par convention, ce premier argument est toujours nommé self : il s'agit de l'objet auquel on appliquera la méthode.

On appelle l'ensemble des méthodes que l'on peut appliquer à un objet l'interface de l'objet. Dans le paradigme de la programmation orientée objet, un programme qui manipule un objet n'est pas censé accéder à la totalité de son contenu (ses attributs) : il doit passer par son interface pour interagir avec lui.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
class Poisson:
    """ Représente un poisson """
    def __init__(self, n, t, f=False):
        """ Poisson, str, int, bool -> None """
        self.nom = n
        self.poids = t
        self.faim = f

    def nourrir(self, repas):
        """ Poisson, int -> None """
        if self.faim:
            self.poids = self.poids + repas
            self.faim = False
            return True
        return False

    def presente(self):
        """ Poisson -> None """
        print(f"Bonjour, je suis {self.nom} et je pèse {self.poids}kg !")
1
2
3
4
5
6
nemo = Poisson("Nemo", 15)
dory = Poisson("Dory", 12)
nemo.presente()
dory.nourrir(7)
dory.nourrir(3)
dory.presente()

À retenir. Si une méthode d'une classe C est du type methode(self, arg1, arg2), et obj est une instance de la classe C, on applique la méthode methode à l'objet obj avec les arguments arg1, arg2 à l'aide de l'instruction obj.methode(arg1, arg2).

Un cas particulier est lorsque la méthode est du type methode(self). On applique alors cette méthode à obj l'aide de l'instruction obj.methode().

  1. Déterminer l'affichage produit par le code ci-dessus. Expliquer votre réponse.

    Bonjour, je suis Nemo et je pèse 15kg !
    Bonjour, je suis Dory et je pèse 19kg !
    
  2. Instancier une variable de type Poisson qui représente le poisson nommé Flipper et qui pèse initialement \(10\) kilos. Écrire le code python qui permet de nourrir Flipper tous les jours de la semaine : son repas vaut 1 le lundi, 2 le mardi, \(\ldots\), 7 le dimanche. À la fin de la semaine, Flipper doit se présenter.

    1
    2
    3
    4
    5
    flipper = Poisson("Flipper", 10)
    for jour in range(1, 7):
        flipper.faim = True
        flipper.nourrir(jour)
    flipper.presente()
    
    Bonjour, je suis Flipper et je pèse 31kg !
    

Méthodes spéciales et erreurs

Méthodes spéciales

Certaines méthodes en python ont un rôle particulier : on les reconnaît à leur nom encadré de deux symboles _ (on les appelle parfois méthodes dunder). C'est par exemple le cas de la méthode __init__ qui est appelée automatiquement à la création d'un nouvel objet de la classe. Une fois ces méthodes définies, il est possible de les appliquer à des objets de la classe avec une syntaxe plus "naturelle".

Méthode Appel Description
__str__(self) str(o) ou print(o) Renvoie une chaîne de caractère décrivant l'objet o
__len__(self) len(o) Renvoie un entier définissant la taille de l'objet o
__lt__(self, a) o < a Renvoie True si o est strictement plus petit que a
__add__(self, a) o + a Renvoie la somme de o et a.

Si la méthode __str__ est définie, alors print(obj) affiche la chaine de caractère renvoyée par obj.__str__(self).

Objets identiques et aliassage

L'utilisation d'objet peut mener au phénomène d'aliassage. Celui-ci a lieu lorsque deux variables font référence au même objet.

1
2
3
4
5
dory = Poisson("Dory", 12)
autre_dory = dory
dory.nourrir(5)
dory.presente()
autre_dory.presente()
Bonjour, je suis Dory et je pèse 17kg !
Bonjour, je suis Dory et je pèse 17kg !

Erreurs classiques

Attribut non défini dans la méthode __init__

1
nemo.taille
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
Input In [431], in <cell line: 1>()
----> 1 nemo.taille

AttributeError: 'Poisson' object has no attribute 'taille'

Confusion entre attribut et méthode

1
nemo.poids()
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
Input In [432], in <cell line: 1>()
----> 1 nemo.poids()

TypeError: 'int' object is not callable

Confusion entre méthode et attribut

1
nemo.presente
<bound method Poisson.presente of <__main__.Poisson object at 0x7d1dcc840230>>