Les classes d’objets




Rappel:

 
Une classe définit un type d’objet. Pour qu’un objet appartienne à ce type, il lui faut posséder certaines propriétés (ou attributs, ou membres).
On peut compléter la définition d’une classe d’objets en lui attribuant des fonctions spécifiques, appelées méthodes: ces méthodes effectuent des opérations sur les membres de l’objet.
 
Exemple: un disque, en géométrie

  • il a un rayon et une position déterminée par les coordonnées de son centre: cela fait 2 membres, une valeur (type float) pour le rayon et un vecteur (type list) de valeurs pour les coordonnées du centre.
  • une méthode, appelée par exemple circonférence() et une autre, appelée aire(), pour calculer ce qu’on devine

Cette page présente successivement

  1. La création et l’instanciation d’une classe personnalisée
  2. la surcharge d’opérateurs.
  3. les méthodes __call__() et __str__().
  4. l’héritage et le polymorphisme

Création et instanciation d’une classe personnalisée

 
La création d’une classe commence par (au minimum)

class <nom de la classe>:

Chaque classe contient au moins une méthode __init__(), qui sert à affecter une valeur initiale à tout ou partie de ses membres. Cette méthode particulière est appelée constructeur. Sans cette initialisation, la classe reste une définition abstraite: c’est le lancement de la méthode __init__() qui crée un objet, appelée instance, à partir du moment où l’on peut effectuer des opérations avec cet objet.
Toutes les méthodes entrant dans la définition d’une classe doivent au minimum prendre la référence self en argument celle-ci désigne l’objet créé. Il faut préciser que, dès la création de la classe, un objet est présent en mémoire, car il faut pouvoir relier une instance aux membres et méthodes qui lui correspondent.
 

 
Pour que le constructeur __init()__ s’exécute, il faut créer une instance d’objet
 

 

L’exemple suivant concerne une classe Vector (Vecteur), dont on crée une instance. Parmi les membres doivent au moins figurer la dimension du vecteur, ses coordonnées, éventuellement des méthodes effectuant des produits scalaires, produits vectoriels, etc.
Les valeurs attribuées à ces membres doivent être passées en arguments du constructeur.
L’argument self est, rappelons-le, obligatoirement passé à chaque méthode, car il désigne l’objet auquel s’applique la méthode.
 

Pour créer un objet de type Vector sans initialiser ses coordonnées , on peut rendre le deuxième argument de __init__() flottant. Par défaut, les coordonnées seront initialisées à 0 dans le constructeur.
 

 
Remarque:
Sachant qu’un tuplet peut être déclaré avec ou sans parenthèses, les deux déclarations suivantes produisent des objets identiques
 

Mais si l’on passe le tuplet sans parenthèses au constructeur de l’exemple précédent, un message d’erreur apparaîtra, car il comprendra qu’on lui passe 6 arguments positionnels au lieu de 3 (incluant self), ce qui n’est pas prévu dans la déclaration de __init()__ (*arg_coordinates compte pour un argument)
La modification du code pour l’enregistrement des valeurs dans la liste self.coordinates en rentrant d’un bloc les éléments de arg_coordinates permet de constater la manière dont le constructeur fonctionne
 

Si l’on veut inclure dans la classe une méthode calculant la norme du vecteur, on code

La surcharge d’opérateurs

 
Les opérateurs de base +,-,*, etc. ont tous une méthode associée. Par exemple,

print ((2).__add__(2))
4 # équivaut à 2+2

Ce code n’est pas modifiable quand il s’agit d’opérations sur des variables élémentaires (2+2 doit toujours donner 4), mais il est personnalisable aux objets créés par le développeur.
Pour additionner deux vecteurs v1 et v2 par exemple, il faut que la méthode __add__() additionne les coordonnées de v1 et v2 deux à deux. L’expression

v1+v2

étant équivalente à

v1.__add__(v2)

__add__() est une méthode appliquée à l’objet v1: l’argument self désigne donc v1. Le deuxième argument désigne par conséquent v2: il est courant de le nommer other

 

 
On peut additionner les objets comme de simples valeurs numériques
 

 

Méthodes inversées

 
L’ordre des arguments dans l’appel d’une méthode reflète la place des objets dans les opérations formelles

v1+v2==v1.__add__(v2)
v2+v1==v2.__add__(v1)

Une méthode de la classe Vector doit s’appliquer à un objet de cette classe, donc le 1er opérande doit être un vecteur. Si on veut personnaliser l’opérateur * (correspondant à la méthode __mul__()) pour permettre la multiplication par un scalaire x,

v.__mul__(x)

sera équivalent à

v*x

Comme l’usage veut que le facteur soit le nombre réel, on voudrait pouvoir écrire x*v, mais les variables simples n’acceptent pas qu’on modifie les méthodes __add__ ou __mul__ (x.__mul__(v) n’est pas autorisé car v n’est pas un nombre)

Pour inverser l’ordre des arguments, on utilise des méthodes prêtes à l’emploi

__radd__() à la place de __add__()
__rmul__() à la place de __mul__()

 
Exemple de multiplication d’un vecteur par un scalaire
 

Ici, self désigne le second opérande, c’est à dire v dans x*v

les méthodes __call__() et __str__()

 

  • __call__(): méthode appelée pour effectuer une valorisation sur une instance d’objet
  • __str__(): méthode pour formater l’affichage d’une instance d’objet

 
Le code suivant définit la classe appelée monôme, c.a.d. un polynôme à un seul coefficient
 

 

Héritage et polymorphisme

 

L’héritage simple

 
L’héritage consiste à intégrer dans la définition d’une classe les membres et méthodes d’une autre classe, appelée classe mère ou parent. La classe qui hérite est appelée classe dérivée: le nom de la classe mère doit être ajoutée entre parenthèses au moment de la création

class <classe dérivée> (<classe mère>):

Exemple simple: soit une classe rectangle, dont dérive une classe parallelepipede


La classe parallelepipede récupère les méthodes __init__ et surface de la classe rectangle. Pour créer un objet de la classe parallelepipede, il suffit de créer une instance de la classe rectangle et d’ajouter un troisième côté. Idem pour le calcul du volume, qui utilise la méthode surface de la classe mère et la valeur du troisième côté.

L’héritage multiple

 
Une classe peut hériter des membres et méthodes de plusieurs classes. Dans l’exemple précédent, si l’on veut créer une classe dédiée au calcul de volumes pour différentes formes, on crée la classe volumes, et on fait hériter les formes de type parallelepipède ou sphère de la méthode volume() appartenant à cette classe.
Cette classe ne contenant qu’une méthode, pas de membre, __init__() se limite à l’instruction pass qui signifie que la fonction n’exécute rien (juste pour respecter les formes)

Si on veut que la classe parallelepipede hérite la méthode volume de cette classe nouvellement créée, on remplace la méthode précédente par la suivante
 

 
Les deux méthodes peuvent ‘cohabiter’ dans la définition de la classe: c’est la méthode héritée de la classe rectangle qui sera appelée en priorité, car chaque instance de parallelepipede est construite à partir de cette classe. La méthode mro affiche les noms des classes dont dérive une classe « fille », en sus d’elle-même et de la classe objet
 

 
Autre exemple: volume d’un objet de la classe sphere