Les classes
Syntaxe
Déclarer une classe est aussi simple que de déclarer une fonction : un mot-clé, suivi du nom de la classe, suivi d’un bloc contenant le code de la classe :
class MaClasse:
"""Documentation"""
On rappelle que l’on prend la bonne habitude de documenter son code et donc ses classes et ses fonctions.
Pour le reste, c’est assez simple. Si l’on déclare une variable au sein de la classe, cette variable est un attribut de la classe. Si l’on déclare une fonction dans la classe, cette fonction est alors une méthode de la classe.
Et, toujours au niveau syntaxique, Python est très permissif. On peut déclarer une classe dans une fonction, une classe dans une classe (ou aussi une fonction dans une fonction, par ailleurs). Après, reste à savoir si cela a une utilité (et c’est le cas), mais on s’en inquiétera pour la partie Les fondamentaux du langage.
Voici comment créer une instance :
mon_instance = MaClasse()
On note seulement la présence des parenthèses pour gérer la construction de l’objet. Il n’est nullement besoin de faire appel à un mot-clé. Pourquoi ? Parce que les langages statiques vont réaliser automatiquement des opérations pour construire l’objet en mémoire et tout le processus est prédictible par rapport aux attributs déclarés dans la classe.
En Python, rien de tout cela. Tout est objet, une instance est un objet parmi un autre et la manière dont il est construit sera définie par le code présent dans la méthode __new__ et non pas par du code figé dans le langage. C’est grâce à cette souplesse que l’on peut modifier la manière dont un objet se crée en Python et ainsi facilement résoudre...
Notion d’instance courante
Dans la plupart des langages de programmation, il existe un mot-clé (généralement this) qui représente l’instance courante. Le langage en lui-même fait un peu de magie en se débrouillant toujours pour retrouver la bonne instance.
En Python, il n’y a pas de magie. L’instance courante n’est pas un mot-clé, mais le premier paramètre de chaque méthode. Ceci est également le fonctionnement de C lorsqu’il crée des bibliothèques de fonctions autour d’une même structure (voir par exemple les API de Gimp).
Cela est un des points les plus déroutants pour ceux qui ont déjà fait de l’objet avec un autre langage. Voici un exemple de méthode :
class MaClasse:
"""Documentation"""
def ma_methode(self, nom):
print("{}.ma_methode({}".format(self, nom)
Voici maintenant un exemple concret d’une classe avec une méthode d’initialisation et une méthode permettant d’afficher l’objet :
class Point:
"""Représente un point dans l'espace"""
def __init__(self, x, y, z):
"""Méthode d'initialisation d'un point dans l'espace"""
self.x = x
self.y = y
self.z = z
def afficher(self):
"""Méthode temporaire utilisée...
Opérateurs
On rappelle qu’en Python, tout est objet. Lorsque l’on utilise un opérateur, Python va en réalité appeler une méthode spéciale de l’opérateur sur l’opérande de gauche et passer l’opérande de droite en paramètre (s’il existe, ce qui dépend de l’opérateur en question).
Il suffit donc de créer une méthode portant un nom spécial pour que l’opérateur associé existe pour la classe.
Exercice : Ajoutez l’opérateur d’addition à la classe Point, sachant qu’il est porté par la méthode spéciale __add__ (et qu’un point s’additionne avec un autre point).
Voici la solution :
class Point:
"""Représente un point dans l'espace"""
[ ... code supprimé ... ]
def __add__(self, other):
return Point(self.x + other.x,
self.y + other.y,
self.z + other.z)
Exercice : Ajoutez l’opérateur de soustraction (méthode __sub__) ainsi que l’opérateur de multiplication (méthode spéciale __mul__), sachant qu’un point se multiplie par un scalaire (nombre).
Exercice : La méthode spéciale __str__ est celle qui est utilisée par print pour afficher un objet, quel que soit cet objet. Surchargez-la pour l’utiliser à la place de la méthode afficher.
La solution est dans le fichier Guide/24_Classes/24__03__opérateurs.py....
Héritage
La manière dont un langage objet gère l’héritage est un de ses marqueurs les plus reconnaissables et les plus fondamentaux. Il y a tellement de pratiques parfois contradictoires et de doctrines sur le sujet qu’il est souvent difficile d’y voir clair.
Il s’agit d’un concept des années 1970 qui a été très largement théorisé et adapté à de nombreux langages qui étaient à l’origine des langages impératifs. Certaines adaptations sont des références (C++ pour C), d’autres sont bancales (PHP, où l’objet est simplement une sémantique et est géré en réalité par un dictionnaire associatif + une liste de fonctions).
Il y a aussi le langage Java qui n’est qu’objet, mais qui a tordu certains concepts. On peut citer par exemple la transformation de la notion d’interface en une manière de faire de l’héritage multiple sans le dire, parce que cela fait peur.
En Python, le langage a été conçu de prime abord pour être multiparadigme avec, entre autres, le support du paradigme objet, et il accepte l’héritage multiple, ce qui signifie qu’une classe peut hériter de plusieurs classes.
Avant de paniquer à la simple mention de ceci, mettons les choses à plat et voyons à quoi peut bien servir l’héritage.
Pour faire simple, l’héritage est un moyen d’éviter de doublonner du code. Et on distingue deux problématiques principales.
1. Spécialisation
Problématique 1 : « J’ai deux objets qui se comportent à peu près de la même façon, mais avec quelques différences. »
Réponse : « Je vais créer une classe pour décrire...