Introduction à l’orienté objet en Python La programmation orientée objet (ou POO en abrégé) correspond à une autre manière d’imaginer, de construire et d’organiser son code. La programmation orientée objet repose sur le concept d’objets qui sont des entités qui vont pouvoir posséder un ensemble de variables et de fonctions qui leur sont propres. Python est un langage résolument orienté objet, ce qui signifie que le langage tout entier est construit autour de la notion d’objets. En fait, quasiment tout en Python est avant tout un objet et nous avons manipulé des objets depuis le début de ce cours sans nous en rendre compte : les types str, int, list, etc. sont avant tout des objets, les fonctions sont des objets, etc. Pour véritablement maîtriser Python et utiliser toutes ses fonctionnalités, il est donc indispensable de comprendre cette composante orienté objet. Qu’est-ce qu’un objet en programmation ? Dans la vie réelle, un objet possède des caractéristiques et nous permet de réaliser des actions. Un crayon par exemple possède une taille, une couleur, une forme, etc. qui sont ses caractéristiques et permet d’écrire ou de dessiner. Le concept d’objets en informatique s’inspire fortement de cette définition de la vie réelle : on va appeler “objet” un bloc cohérent de code qui possède ses propres variables (qui sont l’équivalent des caractéristiques des objets de tous les jours) et fonctions (qui sont nos actions). Comme les objets de la vie courante, les objets informatiques peuvent être très simples ou très complexes. Vous pouvez déjà noter que Python parle “d’attributs” pour désigner les variables et fonctions d’un objet et plus précisément “d’attributs de données” pour désigner les variables d’un objet et de “méthodes” pour désigner les fonctions qui lui sont propres. Comment crée-t-on un objet ? Présentation des classes En POO, un objet ne peut pas être créé ex nihiliste (à partir de rien). La plupart des langages qui supportent l’orienté objet (dont le Python) utilisent d’autres entités pour créer des objets qu’on appelle des classes. Une classe est également un ensemble cohérent de code qui contient généralement à la fois des variables et des fonctions et qui va nous servir de plan pour créer des objets possédant un même ensemble de d’attributs de données et de méthodes de base. 1 Créer une nouvelle classe en Python correspond à définir un nouveau type d’objets ou un nouveau type de données.Pour créer des objets à partir d’une classe en Python, on va utiliser cette classe comme une fonction. Pour illustrer concrètement comment cela fonctionne et pour que vous compreniez plus facilement, créons immédiatement une première classe qu’on va appeler Personne class Personne(object): def __init__(self): self.nom = "Dubois" self.prenom = "Paul" self.lieuDeResidence = "Le Havre" def demenager(self, nouveauLieuDeResidence): self.lieuDeResidence = nouveauLieuDeResidence Détaillons le code ci-dessus : • Notre classe comporte deux fonctions, la fonction __init__ et la fonction demenager. La fonction __init__ est le constructeur, qui permet de définir les variables dans leur état initial. Chaque classe est identifiée à partir d'un nom que vous donnez. Ce nom est à choisir afin qu'il respecte les consignes suivantes : • Le nom de la classe doit être court, mais indiquer son contenu pour faciliter la lecture du code. • Ne doit pas être un nom réservé (and, as,assert, break, class, def, del, if, elif, else, while, for etc.). • Ne doit comporter que des lettres (a → z et A → Z), des chiffres (0 → 9) et le caractère _ (underscore). Les autres symboles sont interdits. • Python est sensible à la casse, c'est-à-dire que les majuscules et minuscules sont distinguées (exemple : livre, Livre et LIVRE sont des variables différentes). • Il est vivement recommandé de nommer les variables en minuscule, commencer les mots avec une majuscule pour plus de lisibilité (exemple : CarteAJouer). On observe l'apparition du mot self au début des variables qui indique que l'on travaille sur l'instance de la classe. 2 Voici un petit tableau présentant la syntaxe des noms de variables dans les classes. Syntaxe Visibilité Portée variable Privée La fonction actuelle seulement self.variable Publique Toute l'instance de l'objet self.__variable Privée Toute l'instance de l'objet Il est également possible de protéger une méthode en la faisant précéder du double symbole souligné (__). Cette méthode ne sera accessible uniquement à l'intérieur de la classe. Une variable privée peut cependant être lue et modifiée via des méthodes spécifiques nommées accesseurs (lecture) et mutateurs (modification). Cela permet d'effectuer des modifications ou des contrôles sur les données avant qu'elles soient retournées ou modifiées. Il est vivement recommandé de procéder ainsi plutôt que d'offrir les variables publiquement. L'exemple suivant permet de mettre en œuvre cette protection des attributs : class Personne: def __init__(self): self.__nom = "Dubois" self.__prenom = "Paul" self.__lieuDeResidence = "Le Havre" def __demenager(self, nouveauLieuDeResidence): self.__lieuDeResidence = nouveauLieuDeResidence def demanderNom(self): return("Je suis " + self.__prenom + " " + self.__nom) Dans l'exemple ci-dessus, la fonction __demenager est accessible uniquement depuis l'objet, alors que la méthode demanderNom est accessible depuis tout le programme. Utiliser un objet Nous avons déjà utilisé des objets. Pour créer une instance d'une classe, nous allons saisir instanceClasse = Classe(). Il est possible de définir les valeurs par défaut des variables de l'instance en les communiquant en argument à la méthode constructeur. Voici un exemple mettant en œuvre l'instanciation d'une classe et le passage d'arguments pour le constructeur : 3 class Personne: def __init__(self, nom, prenom, lieuDeResidence): self.__nom = nom self.__prenom = prenom self.__lieuDeResidence = lieuDeResidence def __demenager(self, nouveauLieuDeResidence): self.__lieuDeResidence = nouveauLieuDeResidence def demanderNom(self): return("Je suis " + self.__prenom + " " + self.__nom) personne1 = Personne("Dupont", "Clara", "Lille") personne2 = Personne("Martin", "Julie", "Béziers") print(personne1.demanderNom()) # Affiche "Je suis Clara Dupont" print(personne2.demanderNom()) # Affiche "Je suis Julie Martin" Les méthodes spéciales Python permet de définir des méthodes spéciales qui permettent de faciliter l'utilisation des objets. Nous allons présenter une méthode déclenchée quand on cherche à convertir notre objet en chaîne de caractères (str() ou print()). Cette méthode doit se nommer __str__. Il est également possible de le faire pour récupérer un entier ou un réel avec respectivement les méthodes __int__ et __float__. En voici un exemple : class Personne: def __init__(self, nom, prenom, lieuDeResidence): self.__nom = nom self.__prenom = prenom self.__lieuDeResidence = lieuDeResidence def __demenager(self, nouveauLieuDeResidence): self.__lieuDeResidence = nouveauLieuDeResidence def __str__(self): return("Je suis " + self.__prenom + " " + self.__nom + " et j'habite à " + self.__lieuDeResidence) personne1 = Personne("Dupont", "Clara", "Lille") personne2 = Personne("Martin", "Julie", "Béziers") print(personne1) # "Je suis Clara Dupont et j'habite à Lille" print(personne2) # "Je suis Julie Martin et j'habite à Béziers" 4 Il est également possible de définir des méthodes spéciales permettant de comparer les objets avec les opérateurs de comparaison. On appelle cela la comparaison riche. Voici la liste des méthodes spéciales associées aux opérateurs de comparaison. Comparateur Syntaxe Méthode associée a égal à b a == b __eq__ a différent de b a != b __ne__ a supérieur à b a>b __gt__ a supérieur ou égal à b a >= b __ge__ a inférieur à b a<b __lt__ a inférieur ou égal à b a <= b __le__ Ces méthodes doivent avoir comme argument l'objet avec lequel comparer l'objet actuel. Voici une implémentation de ces comparateurs riches : class Personne(object): def __init__(self, nom, prenom, age): self.__nom = nom self.__prenom = prenom self.__age = age def getPrenom(self): return(self.__prenom) def getAge(self): return(self.__age) def __lt__(self, autrePersonne): return(self.__age < autrePersonne.getAge()) personne1 = Personne("Dupont", "Clara", 24) personne2 = Personne("Martin", "Julie", 27) if personne1 < personne2: # Utilise personne1.__lt__(personne2) print(personne1.getPrenom() + " est la plus jeune. ") else: print(personne2.getPrenom() + " est la plus jeune. ") 5