Prérequis : fonctions en Python , notion de type de données
À l’issue de ce chapitre, vous saurez :
- définir une classe avec des attributs d’instance et des méthodes ;
- créer des objets et appeler leurs méthodes avec la notation pointée ;
- écrire un constructeur
__init__pour initialiser les attributs ; - utiliser les méthodes spéciales (
__repr__,__add__,__eq__, etc.) pour personnaliser le comportement des objets.
Définitions
La programmation objet consiste à regrouper données et traitements dans une même structure appelée objet. C’est un paradigme - manière de voir - de programmation qui a l’avantage de localiser en un même endroit toute l’implémentation d’une structure de données abstraite.
- Les données associées à un objet sont appelés des attributs.
- Les fonctions ou procédures s’appliquant sur un objet sont appelées des méthodes.
- Une classe est à la fois un modèle d’objet et une machine à fabriquer des objets sur ce modèle.
Un premier exemple : la classe Point
On cherche à définir une classe pour représenter des points à afficher dans un programme de dessin. Chaque point a ses coordonnées x et y. La méthode
translater permet de modifier un point.
- Les attributs d’un point
psont notés usuellement avec la notation pointéep.xetp.y. - L’application d’une méthode est aussi notée par la notation pointée :
p.translater(dx, dy)
Une première classe en Python
La premiere notation à connaître est la notation de classe. Le nom donné à une classe est le nom du constructeur d’objets de cette classe. Par convention, les
noms de classes en Python commencent par une majuscule (Point, CompteBancaire).
Toutes les fonctions définies de manière interne à une classe sont des méthodes. Leur premier paramètre fait référence à l’objet sur lequel est appelée la méthode.
class Point:
"""Manipulation de points."""
def placer(self, abscisse, ordonnee):
"""Place le point aux coordonnées indiquées"""
self.x, self.y = abscisse, ordonnee
def translater(self, dx, dy):
"""Translate le point selon le vecteur (dx, dy)"""
self.placer(self.x + dx, self.y + dy)
On peut créer un objet p par appel du constructeur prédéfini de la classe. On peut ensuite appeler les différentes méthodes sur cet objet avec la notation
pointée. C’est l’objet p qui est alors passé comme valeur du paramètre self.
Remarque 1. En Python, le mot self n’est pas un mot clé. C’est une simple convention d’usage de nommer ainsi le premier paramètre d’une méthode.
p = Point()
p.placer(3, 7)
print(p.x, p.y)
3 7
p.translater(1, 2)
print(p.x, p.y)
4 9
Les attributs x et y sont propres à chaque objet. Dans la terminologie des langages à objet, on parle d’ attributs d’instance.
Remarque 2. En Python, c’est par la notation self.x que l’attribut x est créé pour l’objet sur lequel est appelée la méthode.
Création et initialisation d’un objet en Python
La création standard d’un objet par la méthode de sa classe, crée des objets sans attributs. Pour que les méthodes puissent s’appliquer sans erreur, il faut
cependant que tous les objets d’une classe possèdent certains attributs. Dans l’exemple de la classe Point, la méthode translater ne peut s’appliquer qu’à des
objets ayant les attributs x et y.
C’est pour cette raison, qu’une méthode d’initialisation des objets créant des attributs est nécessaire. En Python, c’est la méthode spéciale __init__, si elle
est définie dans la classe, qui est systématiquement appelée sur chaque nouvel objet juste après sa création.
class Point:
"""Manipulation de points."""
def __init__(self, abscisse, ordonnee):
"""Initialise le point avec les coordonnées indiquées"""
self.x, self.y = abscisse, ordonnee
def translater(self, dx, dy):
"""Translate le point selon le vecteur (dx,dy)"""
self.x += dx
self.y += dy
On peut alors créer un point en fournissant les valeurs des paramètres prévus dans la fonction spéciale d’initialisation.
Si tous les points de cette classe sont créés ainsi, ils disposent alors des attributs usuels d’un point et peuvent être translatés, sans problème.
p = Point(12, 6)
p.translater(1, 2)
print(p.x, p.y)
13 8
Activité. Créer une classe Temps avec des attributs d’instance h, min et s, une méthode d’initialisation et une méthode addition.
Vérifiez votre compréhension
- Quelle est la différence entre une classe et un objet ?
Réponse
Une classe est un modèle (un plan), un objet est une instance concrète de ce modèle. On peut créer autant d'objets qu'on veut à partir d'une même classe, chacun avec ses propres valeurs d'attributs. - Pourquoi la méthode
__init__est-elle importante ?Réponse
Sans__init__, un objet nouvellement créé n'a aucun attribut. Les méthodes qui accèdent à ces attributs provoqueraient une erreur.__init__garantit que chaque objet est dans un état cohérent dès sa création. - Que représente
selfdans une méthode ?Réponse
selfest une référence vers l'objet sur lequel la méthode est appelée. Quand on écritp.translater(1, 2), Python passe automatiquementpcomme valeur deself.
Interface et encapsulation
L’interface d’une classe est l’ensemble des méthodes que l’utilisateur de la classe peut appeler, avec leur spécification (paramètres, valeur renvoyée, effet). C’est le « mode d’emploi » de la classe : pour utiliser un objet Point, il suffit de connaître Point(x, y), translater(dx, dy) et l’addition, sans lire le code des méthodes.
L’encapsulation consiste à regrouper les données (attributs) et les traitements (méthodes) dans l’objet, et à n’accéder aux données qu’à travers les méthodes de l’interface. L’état interne de l’objet reste ainsi cohérent : on ne peut pas mettre un compte bancaire à découvert si le solde n’est modifié que par deposer et retirer, qui vérifient les règles.
class CompteBancaire:
"""Un compte dont le solde ne peut pas devenir négatif."""
def __init__(self, titulaire):
self.titulaire = titulaire
self._solde = 0 # attribut interne : ne pas y toucher directement
def deposer(self, montant):
assert montant > 0, "le montant doit être positif"
self._solde = self._solde + montant
def retirer(self, montant):
assert 0 < montant <= self._solde, "solde insuffisant"
self._solde = self._solde - montant
def solde(self):
return self._solde
Python ne garantit pas l’encapsulation : rien n’interdit d’écrire compte._solde = -1000. La convention est claire : un nom qui commence par _ fait partie de l’implémentation et non de l’interface, on ne l’utilise pas de l’extérieur. Séparer interface et implémentation permet de modifier l’intérieur d’une classe (représentation des données, algorithme) sans changer les programmes qui l’utilisent : c’est le principe que l’on retrouvera avec les structures de données
, dont l’interface (pile, file, arbre) ne dépend pas de l’implémentation choisie.
Classes et objets en Python
En réalité, objets et classes sont omniprésents dans le langage Python, car ils servent à l’implémentation des structures de données et bibliothèques du langage.
On le constate en particulier avec la fonction dir qui liste tous les attributs et méthodes d’un objet.
Cette fonction peut être appelée pour un objet créé par le programmeur.
print(dir(p))
['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'translater', 'x', 'y']
dir peut aussi être appelé pour un objet prédéfini du langage, une liste par exemple. On y découvre toutes les méthodes applicables à une liste.
L = [1, 8, 7]
print(dir(L))
['__add__', '__class__', '__class_getitem__', '__contains__', '__delattr__', '__delitem__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__gt__', '__hash__', '__iadd__', '__imul__', '__init__', '__init_subclass__', '__iter__', '__le__', '__len__', '__lt__', '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__reversed__', '__rmul__', '__setattr__', '__setitem__', '__sizeof__', '__str__', '__subclasshook__', 'append', 'clear', 'copy', 'count', 'extend', 'index', 'insert', 'pop', 'remove', 'reverse', 'sort']
On constate qu’il y a de nombreuses méthodes spéciales repérables par leur nom encadré de __.
Méthodes spéciales en Python
Ces méthodes sont appelées dans des contextes particuliers et peuvent être redéfinies par le programmeur pour une classe particulière.
- A l’initialisation d’un objet, c’est la méthode :
__init__(self, ...) - Lors de la conversion en chaîne par
str(obj)ou affichage avecprint(obj), c’est la méthode :__str__(self) - La conversion en chaîne « évaluable » par
repr(obj)utilise la méthode__repr__(self)qui peut aussi être utilisée parstrouprint, si__str__n’est pas définie. - Le test d’égalité entre deux objets, invoque la méthode
__eq__(self, other) - L’addition d’un objet avec un autre utilise la méthode
__add__(self, other)
Voir ici pour la liste complète.
Suite de l’exemple de la classe Point
On utilise ces méthodes spéciales pour permettre d’afficher une représentation externe d’un point et proposer une addition de deux points.
class Point:
"""Manipulation de points."""
def __init__(self, abscisse, ordonnee):
"""Initialise le point avec les coordonnées indiquées"""
self.x, self.y = abscisse, ordonnee
def translater(self, dx, dy):
"""Translate le point selon le vecteur (dx,dy)"""
self.x += dx
self.y += dy
def __add__(self, other):
return Point(self.x + other.x, self.y + other.y)
def __repr__(self):
return (str(self.x) + "," + str(self.y))
p = Point(1, 5)
q = Point(3, 8)
print(p + q)
4,13
- La POO regroupe données (attributs) et traitements (méthodes) dans un même objet, facilitant l’organisation et la réutilisation du code.
- Une classe définit le modèle ; un objet (ou instance) est une réalisation concrète de ce modèle, avec ses propres valeurs d’attributs.
- La méthode
__init__initialise chaque objet à sa création ;selfdésigne l’objet courant. - L’interface d’une classe (ses méthodes publiques et leur spécification) est ce que l’utilisateur doit connaître ; l’encapsulation impose de passer par ces méthodes pour lire ou modifier les attributs, dont l’implémentation peut changer sans toucher au reste du programme.
- Les méthodes spéciales (
__repr__,__add__,__eq__, etc.) permettent de personnaliser l’affichage, les opérateurs et les comparaisons. - La POO est la brique de base pour implémenter les structures de données du programme (listes chaînées, piles, files, arbres).