UKOnline

Programmation orientée objet

L'objet est au cœur de la programmation orientée objet, par opposition à la programmation procédurale où un programme se construit sur base de fonctions. Ici, on commence par créer un objet, pour ensuite l'utiliser par le biais de ses méthodes.

Identité et état d'un objet

Un objet, une fois créé, possède un état constitué des valeurs de ses attributs. Juste après sa construction, l'objet est dit initialisé, se trouvant dans son état initial. De plus, un objet possède une identité propre, notamment caractérisée par la position où il se trouve en mémoire.

Alors que deux objets différents peuvent tout à fait avoir le même état, ils n'auront jamais la même identité. En Python, on peut utiliser la fonction prédéfinie id pour obtenir l'identité d'un objet. L'exemple ci-dessous crée deux objets différents de type set et affiche leur identité :

Les deux valeurs affichées après exécution sont bien différentes, témoignant des identités distinctes des deux objets créés :

4302577224
4329799752

Dans l'exemple suivant, on crée de nouveau deux objets différents, de type time cette fois-ci, mais qui représentent la même heure. Ces deux objets ont donc le même état, mais gardent des identités différentes :

Deux opérateurs permettent de comparer des objets entre eux. L'opérateur d'égalité (==) permet de tester si deux objets ont le même état. L'opérateur d'identité (is) permet de tester si deux objets ont la même identité. On peut donc, par exemple, écrire :

La première instruction affiche True car les deux objets possèdent exactement le même état, puisqu'ils représentent tous les deux l'instant 12:45:32. Par contre, la seconde instruction affiche False car les deux objets n'ont pas la même identité. Il y a en effet bel et bien deux objets qui ont été créés en mémoire, même s'ils ont le même état :

True
False

Alias

On peut stocker des références vers le même objet dans plusieurs variables différentes, c'est-à-dire créer des alias. On a déjà vu cette notion précédemment, mais revoyons un nouvel exemple :

Dans cet exemple, un objet est créé par la première instruction et une référence vers ce dernier est stockée dans la variable s. La deuxième instruction copie juste le contenu de la variable s dans la variable a, c'est-à-dire la référence vers l'objet comme le montre la figure 5. On dit que la variable a est un alias de la variable s.

Alias
La variable a est un alias de la variable s, c'est-à-dire qu'elles contiennent toutes les deux une référence vers le même objet.

Les deux variables permettent d'agir sur le même objet. Par exemple, si on utilise la variable a pour ajouter un élément dans l'ensemble, puis qu'on affiche le contenu de l'ensemble avec la variable s, on verra que le changement a bien eu lieu sur l'unique objet qui existe en mémoire, référencé par les deux variables :

{'NEW', 9, -5, 12}

Différence tuple nommés/objet

Les objets ressemblent beaucoup aux tuples nommés, ces derniers étant en quelque sorte les précurseurs des objets. Tous les deux permettent de stocker des données, appelées champs pour les tuples nommés et attributs pour les objets, accessibles avec l'opérateur d'accès. Alors qu'un tuple nommé est toujours non modifiable, les attributs d'un objet peuvent être modifiés s'ils ne sont pas en lecture seule.

Si on veut effectuer des opérations sur un tuple nommé, il faut définir une fonction qui reçoit le tuple nommé en paramètre. Pour effectuer des opérations sur un objet, on l'utilise comme objet cible lors de l'appel d'une méthode. La figure 6 résume ces deux types d'appel.

Tuple vs Objet
L'appel d'une fonction qui opère sur un tuple nommé est similaire à l'appel d'une méthode sur un objet. Alors qu'on doit passer le tuple nommé en paramètre de la fonction dans le premier cas, la méthode s'applique sur l'objet cible dans le second cas.

Enfin, une dernière différence entre les tuples nommés et les objets est, qu'avec ces derniers, l'accès aux attributs peut être restreint aux méthodes de l'objet. Ils sont en effet parfois simplement cachés, et dès lors accessibles ni en lecture, ni en écriture. Pour un tuple nommé, ils sont par contre toujours accessibles en lecture.

Tout est objet

En Python, toutes les données sont de type objet, même les nombres entiers, par exemple. La figure 7 montre un objet de type int créé en mémoire et dont une référence est stockée dans la variable temperature. On l'obtient avec l'instruction suivante :

Tout ce qu'on a vu sur les objets s'applique donc aussi sur les types de données qu'on a vu depuis le début du livre. Par exemple, l'instruction x = temperature crée un alias.

Objet de type int
Un nombre entier est un objet en Python, et temperature = 19 contient donc une référence vers un objet de type int représentant le nombre $19$.

Quelles sont les méthodes disponibles pour les objets de type int ? On peut le savoir à l'aide de la fonction prédéfinie dir, qui renvoie une liste comme le montre l'exemple suivant :

['__abs__', '__add__', '__and__', '__bool__', '__ceil__', '__class__', '__delattr__', '__dir__', '__divmod__', '__doc__', '__eq__', '__float__', '__floor__', '__floordiv__', '__format__', '__ge__', '__getattribute__', '__getnewargs__', '__gt__', '__hash__', '__index__', '__init__', '__int__', '__invert__', '__le__', '__lshift__', '__lt__', '__mod__', '__mul__', '__ne__', '__neg__', '__new__', '__or__', '__pos__', '__pow__', '__radd__', '__rand__', '__rdivmod__', '__reduce__', '__reduce_ex__', '__repr__', '__rfloordiv__', '__rlshift__', '__rmod__', '__rmul__', '__ror__', '__round__', '__rpow__', '__rrshift__', '__rshift__', '__rsub__', '__rtruediv__', '__rxor__', '__setattr__', '__sizeof__', '__str__', '__sub__', '__subclasshook__', '__truediv__', '__trunc__', '__xor__', 'bit_length', 'conjugate', 'denominator', 'from_bytes', 'imag', 'numerator', 'real', 'to_bytes']

On voit, par exemple, une méthode real qui permet d'obtenir la partie réelle du nombre et une méthode imag qui permet d'obtenir la partie imaginaire. Dans le cas d'un nombre entier, cette dernière renverra évidemment toujours $0$.

Les méthodes dont les noms sont entourés de __ sont privées et ne devraient pas être utilisées directement, mais indirectement. Par exemple, la méthode __add__ correspond à l'opérateur d'addition (+). Les deux instructions suivantes font exactement la même opération, et affichent toutes les deux $30$ :

Comme expliqué dans le chapitre suivant, Python permet de définir des notations simplifiées pour appeler des méthodes, remplaçant l'appel par l'utilisation d'un opérateur.