Support de cours La programmation orientée objet en python Réaliser par : Nene SYLLA 1 1. Le concept de POO en Python La programmation orienté objet est un type de programmation basée sur la création des classes et des objets via une méthode appelée instanciation. Une classe est un prototype (modèle) codé en un langage de programmation dont le but de créer des objets dotés d’un ensemble de méthodes et attributs qui caractérisent n’importe quel objet de la classe. Les attributs sont des types de données (variables de classe et variables d’instance) et des méthodes, accessibles via la concaténation par points. En programmation orientée objet, la déclaration d’une classe regroupe des méthodes et propriétés (attributs) communs à un ensemble d’objets. Ainsi on pourrait dire qu’une classe représente une catégorie d’objets. Elle apparaît aussi comme une usine permettant de créer des objets ayant un ensemble d’attributs et méthodes communes. Depuis sa création, Python est un langage de programmation orienté objet. Pour cette raison, la création et l’utilisation de classes et d’objets en Python est une opération assez simple. 2. Les classes en Python Pour créer une classe en Python, on utilise l’instruction : class nom_de_la_classe : On crée ensuite une méthode qui permet de construire les objets, appelé constructeur via l’instruction : def__init__ ( self ) : Exemple 1: Créons la class Personne qui prendra comme attributs : nom et age class Personne : def __init__ ( self ,nom, age) : self.nom = nom self.age = age P = Personne( ” Albert ” ,27) print ( ”Le nom de la personne est : ”, P.nom) print ( ”L’age de la personne est : ”, P.age, ” ans” ) # affiche : Le nom de la personne est : Albert 2 # L’age de la personne est : 27 ans Exemple 2 : Créons la classe Rectangle qui prendra comme attributs : Longueur et Largeur class Rectangle : def __init__ ( self , L , l ) : s e l f . Longueur=L s e l f . Largeur=l monRectangle=Rectangle(7 ,5) print ( ”La longueur de mon rectangle est : ” ,monRectangle . Longueur) print ( ”La largeur de mon rectangle est : ” ,monRectangle . Largeur ) Ce qui affiche à l’exécution : La longueur de mon rectangle est : 7 La largeur de mon rectangle est : 5 3. Les méthodes de classes en Python Une méthode de classe est une fonction ou procédure nommée au sein de la classe, permettant de définir des propriétés ou comportements des objets d’instances. Exemple : Reprenons l’exemple de la class Rectangle et ajoutons une méthode qui calcule la surface de ce rectangle. class Rectangle : def __init__ ( self , L , l ) : self.Longueur = L self.Largeur = l # méthode qui calcule la surface def surface ( self) : return self.Longueur*self.Largeur # création d’un rectangle de longueur 7 et de largeur 5 monRectangle = Rectangle(7 ,5) print ( ”La surface de mon rectangle est : ”, monRectangle.surface ()) Ce qui affiche après exécution : La surface de mon rectangle est : 35 3 4. L’encapsulation en python L'encapsulation est un principe qui consiste à cacher ou protéger certaines données de notre objet. Dans la plupart des langages orientés objet, tels que le C++, le Java ou le PHP, on va considérer que nos attributs d'objets ne doivent pas être accessibles depuis l'extérieur de la classe. Autrement dit, vous n'avez pas le droit de faire, depuis l'extérieur de la classe, mon_objet.mon_attribut. On va définir des méthodes un peu particulières, appelées des accesseurs et mutateurs. Les accesseurs donnent accès à l'attribut. Les mutateurs permettent de le modifier. Concrètement, au lieu d'écrire mon_objet.mon_attribut, vous allez écrire mon_objet.get_mon_attribut(). De la même manière, pour modifier l'attribut écrivez mon_objet.set_mon_attribut(valeur) et non pas mon_objet.mon_attribut = valeur. get signifie « récupérer », c'est le préfixe généralement utilisé pour un accesseur. set signifie, dans ce contexte, « modifier » ; c'est le préfixe usuel pour un mutateur. Il peut être très pratique de sécuriser certaines données de notre objet, par exemple faire en sorte qu'un attribut de notre objet ne soit pas modifiable, ou alors mettre à jour un attribut dès qu'un autre attribut est modifié. Les cas sont multiples et c'est très utile de pouvoir contrôler l'accès en lecture ou en écriture sur certains attributs de notre objet. L'inconvénient de devoir écrire des accesseurs et mutateurs, comme vous l'aurez sans doute compris, c'est qu'il faut créer deux méthodes pour chaque attribut de notre classe. D'abord, c'est assez lourd. Ensuite, nos méthodes se ressemblent plutôt. Certains environnements de développement proposent, il est vrai, de créer ces accesseurs et mutateurs pour nous, automatiquement. Mais cela ne résout pas vraiment le problème, vous en conviendrez. En Python, il n'y a pas d'attribut privé. Tout est public. Cela signifie que si vous voulez modifier un attribut depuis l'extérieur de la classe, vous le pouvez. Pour faire respecter l'encapsulation propre au langage, on la fonde sur des conventions que nous allons découvrir un peu plus bas mais surtout sur le bon sens de l'utilisateur de notre classe (à savoir, si j'ai écrit que cet attribut est inaccessible depuis l'extérieur de la classe, je ne vais pas chercher à y accéder depuis l'extérieur de la classe). Les propriétés sont un moyen transparent de manipuler des attributs d'objet. Elles permettent de dire à Python : « Quand un utilisateur souhaite modifier cet attribut, fais cela ». De cette façon, on peut rendre certains attributs 4 tout à fait inaccessibles depuis l'extérieur de la classe, ou dire qu'un attribut ne sera visible qu'en lecture et non modifiable. Ou encore, on peut faire en sorte que, si on modifie un attribut, Python recalcule la valeur d'un autre attribut de l'objet. Les propriétés sont des objets un peu particuliers de Python. Elles prennent la place d'un attribut et agissent différemment en fonction du contexte dans lequel elles sont appelées. Si on les appelle pour modifier l'attribut, par exemple, elles vont rediriger vers une méthode que nous avons créée, qui gère le cas où « on souhaite modifier l'attribut ». Création de propriété Une propriété ne se crée pas dans le constructeur mais dans le corps de la classe. Il s'agit d'une classe, son nom est property. Elle attend quatre paramètres, tous optionnels : o la méthode donnant accès à l'attribut ; o la méthode modifiant l'attribut ; o la méthode appelée quand on souhaite supprimer l'attribut ; o la méthode appelée quand on demande de l'aide sur l'attribut. En pratique, on utilise surtout les deux premiers paramètres : ceux définissant les méthodes d'accès et de modification, autrement dit nos accesseur et mutateur d'objet. Exemple : #−*− coding : utf−8 −*− class Personne : def __init__ ( self ,nom, age) : self.nom = nom self.age = age self._lieu_residence = ‘’Mali’’ # Notez le souligne _ devant le nom def _get._lieu_residence (self): print (“On accede a l’attribut lieu_residence’’) return self._lieu_residence def _set._lieu_residence (self, nouvelle_residence): print ("Attention, il semble que {} déménage à {}.".format( \ self.prenom, nouvelle_residence)) self._lieu_residence = nouvelle_residence # On va dire à Python que notre attribut lieu_residence pointe vers une #propriété 5 lieu_residence = property(_get_lieu_residence, _set_lieu_residence) Explication de l’exemple : o Tout d'abord, dans le constructeur, on ne crée pas un attribut self.lieu_residence mais self._lieu_residence. Il n'y a qu'un petit caractère de différence, le signe souligné _ placé en tête du nom de l'attribut. Et pourtant, ce signe change beaucoup de choses. La convention veut qu'on n'accède pas, depuis l'extérieur de la classe, à un attribut commençant par un souligné _. C'est une convention, rien ne vous l'interdit… sauf, encore une fois, le bon sens. o On définit une première méthode, commençant elle aussi par un souligné _, nommée _get_lieu_residence. C'est la même règle que pour les attributs : on n'accède pas, depuis l'extérieur de la classe, à une méthode commençant par un souligné _. Si vous avez compris ma petite explication sur les accesseurs et mutateurs, vous devriez comprendre rapidement à quoi sert cette méthode : elle se contente de renvoyer le lieu de résidence. Là encore, l'attribut manipulé n'est pas lieu_residence mais _lieu_residence. Comme on est dans la classe, on a le droit de le manipuler. o La seconde méthode a la forme d'un mutateur. Elle se nomme _set_lieu_residence et doit donc aussi être inaccessible depuis l'extérieur de la classe. À la différence de l'accesseur, elle prend un paramètre : le nouveau lieu de résidence. En effet, c'est une méthode qui doit être appelée quand on cherche à modifier le lieu de résidence, il lui faut donc le nouveau lieu de résidence qu'on souhaite voir affecté à l'objet. o Enfin, la dernière ligne de la classe est très intéressante. Il s'agit de la définition d'une propriété. On lui dit que l'attribut lieu_residence (cette fois, sans signe souligné _) doit être une propriété. On définit dans notre propriété, dans l'ordre, la méthode d'accès (l'accesseur) et celle de modification (le mutateur). Quand on veut accéder à objet.lieu_residence, Python tombe sur une propriété redirigeant vers la méthode _get_lieu_residence. Quand on souhaite modifier la valeur de l'attribut, en écrivant objet.lieu_residence = valeur, Python appelle la méthode _set_lieu_residence en lui passant en paramètre la nouvelle valeur. 5. Héritage en Python Pour éviter de recopier le code d’une classe, on utilise la méthode d’héritage. La méthode d’héritage consiste à créer à partir d’une classe mère une autre classe appelé classe fille qui hérite toutes les méthodes et propriétés de la classe mère. 6 Pour simplifier l’acquisition pour les apprenants débutant, nous allons traiter ce concept sur un exemple simple : Classe mère : #−*− coding : utf−8 −*− class Personne : def __init__ ( self ,nom, age) : self.nom = nom self.age = age Nous venons de définir une classe Personne dont les attributs sont : nom et age. Nous allons maintenant créer une classe fille nommée Student qui hérite les mêmes méthodes et propriétés de la classe mère Personne. La syntaxe générale de l’héritage se fait grâce à la commande : class classe_fille (classe_mère): Qui veut dire que la classe classe_fille hérite de la classe classe_mère. Exemple pour notre cas de la classe fille Student qui hérite de la classe mère Personne : class Student(Personne) : L’héritage des attributs nom et age se fait via la commande : Personne.__init__( self ,nom,age) Code de la classe fille Student : #−*− coding : utf−8 −*− class Student(Personne) : # definition des attributs def __init__ ( self ,nom, age, filliere ) : # héritage des attributs depuis la classe mère Personne Personne. __init__ ( self , nom, age) # ajout d’un nouvel attribut filière à la classe fille self.filiere = filiere 7 Code complet: #−*− coding : utf−8 −*− class Personne : def __init__ ( self ,nom, age) : self.nom = nom self.age = age # La classe fille Student hérite de la classe mère Personne class Student(Personne) : # definition des attributs def __init__ ( self ,nom, age, filiere ) : # héritage des attributs depuis la classe mère Personne Personne . __init__ ( self , nom, age) # ajout d’un nouvel attribut filière à la classe fille self.filiere = filiere Stud = Student( ” Albert ” ,27 , ”math” ) print ( ”Le nom de l’étudiant est : ”,Stud .nom) print ( ”L’age de l’étudiant est : ”,Stud . age) print ( ”La filiere de l’étudiant est : ”,Stud . filiere) Ce qui affiche après exécution : Le nom de l’étudiant est : Albert L’age de l’étudiant est : 27 La filière de l’étudiant est : math 8