Syntaxe POO en Python
Temps de lecture10 minComme nous l’avons vu dans le cours d’introduction aux concepts de la POO, une classe est une sorte de modèle pour créer des objets. C’est dans la classe que nous définirons nos méthodes et attributs.
La POO est particulièrement utile lorsque vous devez représenter des données un peu plus complexes qu’un simple nombre ou une chaîne de caractères. Bien sûr, il existe des classes que chaque langage de programmation (y compris Python) définit pour nous : les nombres, les chaînes et les listes en font partie. Mais nous serions limités si nous ne pouvions pas créer nos propres classes.
Définition de classe
Pour introduire la syntaxe, créons une première classe très simple représentant une personne (classe Person). Pour nous ici, une personne est caractérisée par le nom de famille, le prénom, l’âge et le lieu de résidence (ville). La classe aura donc 4 attributs et un constructeur.
La création de cette classe correspond au code ci-dessous.
Examinons la syntaxe en détail :
-
La classe est définie par le mot-clé
class, suivi du nom de la classe et des deux-points rituels:. Par convention, les noms de classe commencent par une majuscule. -
Une
docstringcommentant la classe. C’est optionnel, mais fortement recommandé. Elle est placée juste après le nom de la classe et entourée de trois guillemets""". -
La définition du constructeur. Le nom d’un constructeur est toujours
__init__et le premier paramètreself. Nous verrons plus tard ce que cela signifie. Les autres paramètres sont séparés par,comme avec les fonctions en programmation procédurale. -
Le constructeur contient la déclaration et l’initialisation des attributs de la classe. Ici, une personne est créée avec un nom, un prénom, un âge et la ville où elle vit.
Créez un fichier person.py et copiez le code ci-dessus. Si vous essayez de l’exécuter, rien ne se passe. Vous venez de définir la classe, nous devons l’instancier avant de pouvoir l’utiliser.
Instanciation de classe : créer des objets
Nous voulons créer deux personnes.
Copiez/collez le code ci-dessus à la fin du fichier person.py en prenant garde à l’indentation. La condition if __name__ == '__main__' doit se trouver le plus à gauche possible.
Cette condition permet de s’assurer que le code à l’intérieur de ce bloc ne sera exécuté que lorsque le script est exécuté directement, et non lorsqu’il est importé en tant que module dans un autre script.
Vous trouverez plus d’informations à ce sujet dans la documentation officielle.
Exécutez-le. Maintenant, vous pouvez voir quelque chose de peu compréhensible… Ici, le plus important est de voir la classe d’où vient l’objet. Nous pouvons donc vérifier que les deux objets proviennent bien de notre classe Person. Vous pouvez ajouter ce genre de tests de fonctionnement dans chaque classe en attendant que l’on vous présente une méthode plus élégante pour tester chaque fonctionnalité de votre classe.
Nous allons maintenant ajouter une méthode qui retourne une chaîne représentant une personne, et nous l’appellerons après avoir instancié la classe.
Comme vous pouvez le voir dans le code ci-dessus, la définition d’une méthode dans une classe est similaire à la définition d’une fonction classique, à part l’existence du mot-clé self comme premier argument de la méthode. L’appel de méthode se fait en précédant le nom de la méthode par l’objet sur lequel la méthode doit être appelée. Dans notre exemple, one_person.to_string() exécute le code de la méthode to_string() de l’objet one_person.
Détails
Python offre ce qu’on appelle des méthodes spéciales. Ce sont des méthodes que Python reconnaît et sait comment utiliser. Le nom d’une telle méthode prend une syntaxe spéciale : __methodspeciale__. En fait, vous en connaissez déjà une, __init__. Une autre méthode de ce type est __str__ (qui ne prend aucun argument à part self comme toute autre méthode), qui est appelée par Python pour afficher un objet. Vous pouvez tester ceci en ajoutant la méthode __str__(self) à notre classe Person et en utilisant print(one_person) pour voir le résultat.
De même, la méthode __repr__ est appelée par Python pour afficher un objet dans la console. Vous pouvez tester ceci en ajoutant la méthode __repr__(self) à notre classe Person et en utilisant one_person pour voir le résultat.
__str__ est utilisée pour afficher l’objet à l’utilisateur de manière lisible.
__repr__ fournit une représentation complète sous forme de chaîne de l’objet, qui est utile pour le débogage, la journalisation et le développement. Ainsi, elle est plus pour les développeurs que pour les utilisateurs.
Maintenant nous pouvons parler du mot-clé self. Quand vous créez un objet, dans ce cas une personne, les valeurs de ses attributs lui sont propres (la valeur de l’attribut name de one_person n’est pas la même que celle de a_second_person). C’est logique, car chaque personne a son propre nom, etc. Donc quand vous appelez une méthode depuis un objet, il est important de savoir sur quel objet elle doit être exécutée et c’est le rôle du mot-clé self.
Encapsulation
Examinons maintenant le concept d’encapsulation. Pour rappel, ce concept consiste à protéger ou cacher certains attributs d’un objet.
En Python, par défaut, la valeur de l’attribut d’un objet est directement accessible : il suffit d’écrire my_object.my_attribute et c’est fait (essayez le code ci-dessous dans votre fichier person.py). Il y a deux stratégies pour rechercher l’encapsulation des attributs :
-
utiliser des identifiants d’attributs sous la forme
_my_attribute. Ce _single_leading_underscore est juste une convention utilisée en Python pour indiquer des attributs qui ne sont pas supposés être lus ou écrits depuis l’extérieur de la classe. Mais ce n’est qu’une convention ! rien ne les empêche d’être utilisés directement en dehors de la classe. Vous pouvez lire la documentation pour plus d’informations. -
utiliser des identifiants d’attributs sous la forme
__my_attribute. Cela renforce l’encapsulation, car la valeur de tels attributs ne peut pas être directement accessible depuis l’extérieur de la classe. Cependant, l’encapsulation n’est pas vraiment implémentée car Python transforme ces noms avec le nom de la classe :__my_attributedans la classeClasspeut être accessible depuis l’objetoen appelanto._Class__my_attribute.
Voyons en pratique pour notre classe Person.
Information
Les formes avec un et deux traits de soulignement en début peuvent aussi être utilisées lors de la nomination de méthodes pour indiquer des méthodes non-publiques. Voir la documentation pour plus d’informations.
Héritage
L’héritage est une façon de structurer le code qui définit des relations hiérarchiques entre les classes. Pour illustrer comment créer des classes qui héritent d’une autre, supposons que nous voulions créer un programme qui manipule des chiens et des chats. Les deux ont un surnom et nous voulons suivre le nombre de repas qu’ils ont mangé depuis leur naissance. Cependant, les chiens aboient et les chats miaulent et nous voulons suivre le poids d’un chien.
Comme les chiens et les chats ont des caractéristiques communes (surnom et nombre de repas), nous décidons de créer une classe Animal avec deux attributs (un pour stocker le nom de l’animal et l’autre pour compter le nombre de fois qu’il a mangé) et une méthode, exécutée chaque fois que l’animal mange quelque chose.
Ci-dessous vous trouverez le code de la classe Animal.
Maintenant, comment les chiens et les chats sont-ils réifiés dans notre programme ? D’un côté, en tant qu’animal, un chien devrait avoir un surnom et le nombre de fois qu’il a mangé mais un chien a aussi un poids et peut aboyer. D’un autre côté, un chat est aussi un animal mais il miaule aussi. Par conséquent, nous allons implémenter les classes Dog et Cat comme enfants de la classe Animal pour qu’elles aient automatiquement accès à ses attributs et méthodes. La classe Dog aura un nouvel attribut (weight) et une nouvelle méthode (bark) et la classe Cat fournira une méthode meow. Le code pour ces classes est présenté ci-dessous.
Essayons ce code :
Pour aller plus loin
Certains langages comme Python permettent l’héritage multiple, c’est-à-dire qu’une classe hérite des propriétés et des méthodes de plusieurs classes parentes.
Exercice pratique : Héritage multiple
Pour expérimenter avec l’héritage multiple, essayez de créer une classe Human qui a la capacité de parler, puis créez une classe Cheshire (chat du Cheshire d’Alice au Pays des Merveilles) qui hérite à la fois de Cat et de Human. Cette classe devrait pouvoir :
- Miauler comme un chat (hérité de
Cat) - Parler comme un humain (hérité de
Human) - Avoir un surnom et compter ses repas (hérité de
AnimalviaCat)
Voici un exemple de structure pour vous aider à démarrer :
Testez votre implémentation avec ce code :
Conseil
En Python, l’ordre des classes parentes dans la déclaration d’héritage multiple (class Cheshire(Cat, Human)) détermine l’ordre de résolution des méthodes (Method Resolution Order - MRO). Vous pouvez utiliser Cheshire.mro() pour voir dans quel ordre Python recherche les méthodes. Faites des tests avec une méthode de même nom définie dans les deux classes parentes Cat et Human.
Pour aller encore plus loin
Python est un langage très permissif laissant toute liberté aux développeurs. Certains aspects de la POO, comme la protection de propriétés de classe par encapsulation, ne sont pas strictement définis et respectés. Par exemple, il est, par convention, conseillé de préfixer une propriété protégée par un _. Ceci n’est qu’informatif car la propriété reste accessible en dehors de la classe et de ses sous-classes.
Des mécanismes de décorateurs permettent cependant d’ajouter ce contrôle d’accès. Essayer de protéger l’accès à une propriété de classe à l’aide des décorateurs @property, @setteret @getter. Exploitez notamment la documentation suivante :