Modèle objet
Tout est objet
1. Principes
a. Quel sens donner à « objet » ?
Python est un langage qui utilise plusieurs paradigmes et parmi ceux-ci, le paradigme objet. Ce dernier a été élaboré dans les années 1970 et est avant tout un concept. Un objet représente :
-
un objet physique :
-
parcelle de terrain, immeuble, appartement, propriétaire, locataire… ;
-
voiture, pièces d’une voiture, conducteur, passager… ;
-
bibliothèque, livre, page d’un livre… ;
-
périphérique matériel, robot… ;
-
un objet informatique :
-
fichier (image, document textuel, son, vidéo…) ;
-
service (serveur, client, site Internet, web service…) ;
-
un flux de données, pool de connexions… ;
-
un concept :
-
porteur d’une notion qu’il peut partager ;
-
séquenceur, ordonnanceur, analyseur de données...
Nous en profitons pour attirer l’attention du lecteur sur certains aspects cognitifs : à partir du moment où l’on modélise des personnes comme objets, on peut très facilement oublier que derrière du code, il y a des êtres vivants et pensants, dont la liberté peut être restreinte par des choix que l’on fait sur la manière de modéliser, la quantité ou la qualité des informations que l’on décide de traiter et/ou de conserver.
Voici un exemple très simple et concret : un gestionnaire d’emploi du temps qui ne permet de créer des créneaux que par demi-heure peut avoir des impacts réels sur le fonctionnement d’un service qui, lui, a besoin d’une granularité plus fine et donc impacter très négativement les utilisateurs qui, avant l’arrivée du logiciel, avaient une plus grande...
Autres outils de la programmation objet
1. Principes
En Python, l’essentiel de la programmation objet repose sur la bonne déclaration des classes, sur la souplesse accrue du langage lui-même qui permet de plier les classes, les instances, leurs attributs et leurs méthodes à souhait et aux autres qualités développées dans les deux chapitres précédents.
La maîtrise de ce qui a été exposé dans la section Tout est objet, nous permet d’écrire facilement des composants efficaces et les architecturer conformément à nos attentes. Il s’agit de fonctionnalités légères, non contraignantes, très agiles et suffisantes pour répondre à tous les cas d’utilisation.
Pour les débutants, ceci est suffisant et même dans beaucoup de cas, pour les personnes expérimentées, rares sont les besoins d’utiliser d’autres concepts.
Mais Python est un langage très complet et il permet d’offrir des fonctionnalités plus complexes et plus complètes sans pour autant les imposer et rendre son modèle objet contraignant. La liberté de choix du développeur est une règle de sa philosophie, mais liberté de choix ne signifie pas uniquement « pas de contraintes », cela signifie également un panel de choix important et utile.
2. Interfaces
Maintenant que l’on sait écrire des classes, les organiser, gérer les attributs et les méthodes, il est temps de les faire dialoguer entre elles. Or, une des problématiques consiste à déterminer avec quel type de classe l’on peut discuter.
Pour cela, Python se fonde sur le « duck typing ». Si cela marche comme un canard et cela cancane comme un canard, alors cela doit être un canard. Autrement...
Fonctions spéciales et primitives associées
1. Personnalisation
a. Classes
Il est possible de personnaliser les classes par la bonne utilisation de la méthode spéciale __new__ et par l’utilisation des métaclasses, vues précédemment dans ce chapitre. En ce sens, Python 3 a beaucoup amélioré, simplifié et homogénéise son comportement par rapport à l’ancienne branche.
Cette méthode spéciale est une méthode de classe (son premier argument est la classe). C’est elle qui crée une instance de la classe courante et appelle sa méthode __init__ qui est une méthode d’instance.
Les autres arguments passés aux deux méthodes sont alors identiques.
La surcharge de __new__ permet donc de personnaliser la manière dont l’instance est créée alors que la surcharge de __init__ permet de personnaliser l’instance elle-même, par le positionnement d’attributs, par exemple.
Voici une démonstration de l’ordre dont les méthodes sont appellées :
>>> class A:
... def __new__(cls, info):
... print('A\tNew\t%s\t\t\t%s' % (cls, info))
... return object.__new__(cls, info)
... def __init__(self, info):
... print('A\tInit\t%s\t%s' % (self, info))
... return object.__init__(self, info)
...
>>> class B(A):
... def __new__(cls, info):
... print('B\tNew\t%s\t\t\t%s' % (cls, info))
... return...