3.7. Classes

3.7.1. Introduction

3.7.1.1. Définition

Un objet est construit à partir d’une classe. On dit que l’on crée une instance d’une classe. Les classes peuvent être vues comme une description de l’objet, qui rassemble ses propriétés (les attributs) et comment agir sur ces dernières (les méthodes).

Une classe est déclarée à l’aide du mot-clé class, suivi d’un nom puis de deux points. Suit ensuite un bloc d’instructions. De même que pour les fonctions, il est possible de définir une docstring.

class Name:
    '''Docstring.'''
    instructions

Note

Par convention, les noms des classes sont en CamelCase.

Une classe est un objet callable : ainsi, pour créer un objet à partir d’une classe, il faut écrire son nom suivi de parenthèses.

>>> class Vector:
...     pass
>>> v = Vector()
>>> v
<Vector object at ...>

Note

Il est possible de mettre des parenthèses après le nom de la classe, mais ceci est tout à fait optionnel : class Name(): est tout aussi correct que class Name:.

3.7.1.2. Attributs

Il est possible d’accéder aux attributs d’un objet grâce au point. Il est aussi possible de définir un attribut par simple assignation.

>>> class Vector:
...     x = 3
...     y = 4
>>> v = Vector()
>>> v.x, v.y
(3, 4)
>>> v.z = 6
>>> v.z
6
>>> v.z + 3
9
>>> del v.z
>>> v.x = v.x + 4
>>> v.x
7

Note

Bien qu’il soit possible de créer ou modifier directement un attribut en Python, il est plutôt déconseillé de le faire : mieux vaut passer par des méthodes dédiées à cet effet.

Il est possible d’accéder récursivement aux attributs d’un objet en utilisant plusieurs points.

>>> class CVector:
...     pass
>>> cv = CVector
>>> cv.affix = 1+3j
>>> cv.affix
(1+3j)
>>> cv.affix.imag
3.0

Les attributs d’une classe sont encapsulés dans celle-ci et ne sont donc pas visible depuis l’extérieur.

>>> v.x
7
>>> x
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'x' is not defined

3.7.1.3. Méthodes

Une méthode consiste simplement en une fonction définie à l’intérieur de la classe et dont le premier paramètre est toujours une référence à l’instance (par convention, l’on utilise selfsoi-même en anglais).

Ainsi self désigne, dans le corps de la méthode, l’instance qui contient la méthode.

>>> class Vector:
...     x = 3
...     y = 4
...     def norm(self):
...         return (self.x**2 + self.y**2)**(1/2)
...     def homothety(self, n):
...         self.x = self.x * n
...         self.y = self.y * n
>>> v = Vector()
>>> v.norm()
5.0
>>> v.homothety(3)
>>> v.x, v.y
(9, 12)

3.7.1.4. Constructeur

Le constructeur est une méthode spéciale (il en existe d’autres, que nous verrons par la suite), appelée __init__(), qui est appelée lors de l’instanciation de l’objet : cela permet d’effectuer diverses opérations sur chaque nouvel objet à leur création. Par exemple, si l’on reprend le cas précédent, il n’est pas très intéressant d’avoir un vecteur dont les attributs x et y sont déjà définis. Nous pouvons y remédier ainsi :

>>> class Vector:
...     def __init__(self, x=0, y=0):
...         self.x = x
...         self.y = y
...     def norm(self):
...         return (self.x**2 + self.y**2)**(1/2)
>>> v1 = Vector(3, 4)
>>> v1.x, v1.y
(3, 4)
>>> v2 = Vector(-2, 7)
>>> v2.x, v2.y
(-2, 7)

3.7.1.5. Éléments publics et privés

Contrairement à d’autres langages, les concepts de membres privés ou protégés n’existent pas réellement en Python : certaines conventions indiquent qu’un élément est privé, mais l’utilisateur peut toujours choisir d’y accéder. Ainsi, Python part du principe que l’utilisateur n’accédera pas n’importe comment aux éléments.

Un membre protégé commence par un simple underscore, tandis qu’un membre privé commence par deux underscores.

3.7.2. Méthodes spécifiques

3.7.2.1. Destructeur

La méthode destructeur, nommée __del__() est appelée lorsque l’on détruit l’objet avec del.

3.7.2.2. Opérateurs

Il est possible de surcharger les opérateurs pour une classe, ce qui permet de définir le comportement des objets lorsqu’on les manipule avec des opérateurs. Lles méthodes associées aux opérateurs sont : __add__() pour l’addition, __sub__() pour la soustraction, __mul__() pour la multiplication, __truediv__() pour la division, __floordiv__() pour la division entière, __mod__() pour le modulo, __pow__() pour la puissance.

>>> class Vector:
...     def __init__(self, x, y):
...         self.x, self.y = x, y
...     def __add__(self, other):
...         if isinstance(other, Vector):
...             return Vector(self.x + other.x, self.y + other.y)
>>> v1 = Vector(2, 3)
>>> v2 = Vector(-4, 7)
>>> v3 = v1 + v2
>>> v3.x, v3.y
(-2, 10)

3.7.3. Héritage

3.7.3.1. Introduction

Il est aisé de dériver une classe : pour ce faire, il suffit d’indiquer la ou les classes parentes (séparées par des virgules) entre parenthèses après le nom de la classe enfant.

class Name(ParentName1, ..., ParentNamen):
    instructions

Il est alors possible d’accéder à chaque attribut des classes parentes.

>>> class Fruit:
...     def __init__(self, color):
...         self.color = color
>>> class Appel(Fruit):
...     pass
>>> a = Appel('red')
>>> a.color
'red'

Il est possible de dériver une classe qui est déjà dérivée d’une autre classe, et ce autant de fois que l’on souhaite.

Si la classe enfant redéfinit des attributs ou méthodes du parent, alors ceux-ci les remplacent.

3.7.3.2. Polymorphisme

3.7.4. Exercices

  1. Écrire une classe qui permettra de gérer un fichier (dont le nom sera passé au constructeur). Elle devra comporter plusieurs méthodes qui réaliseront des opérations sur le fichier :

    • count : renvoie le nombre de lignes, de mots et de lettres.
    • head(n) : renvoie les n premières lignes.
    • tail(n) : renvoie les n dernières lignes.
    • find(str) : afficher toutes les lignes contenant la chaine str.
    • replace(str1, str2) : afficher à l’écran toutes les lignes du fichier, en remplaçant chaque occurrence de str1 par str2.
    • tout ce qui pourra vous passer par la tête (mais qui ne modifie pas le fichier lui-même).

    Il faudra veiller à ne pas charger en mémoire tout le contenu du fichier, ni à le conserver ouvert trop longtemps.

  2. Proposer une amélioration du système de droits (exercice 4 du chapitre Types de base) en utilisant les classes.