cours
Encapsulation dans la programmation orientée objet en Python : Un guide complet
Pourquoi apprendre l'encapsulation ?
L'encapsulation est un principe fondamental de l'orientation objet en Python. Il protège vos classes contre les modifications ou suppressions accidentelles et favorise la réutilisation et la maintenabilité du code. Considérez cette simple définition de classe :
class Smartphone:
def __init__(self, brand, os):
self.brand = brand
self.os = os
iphone = Smartphone("Apple", "iOS 17")
De nombreux programmeurs Python définissent les classes de cette manière. Cependant, il est loin des meilleures pratiques que suivent les Pythonistes professionnels. Le problème de cette classe est évident lorsque vous essayez de modifier ses données :
iphone.os = "Android"
print(iphone.os)
Android
Imaginez un iPhone fonctionnant sous Android - quel scandale ! Il est clair que nous devons fixer des limites à notre classe afin que les utilisateurs ne puissent pas modifier ses attributs à leur guise.
S'ils décident de les modifier, ils doivent le faire dans le respect de nos conditions et de nos règles. Mais nous voulons que les attributs .os
ou .brand
restent les mêmes à la surface.
Toutes ces choses peuvent être favorisées grâce à l'encapsulation et dans ce tutoriel, nous allons tout apprendre à ce sujet. Commençons !
Comment l'encapsulation est-elle réalisée en Python ?
Python prend en charge l'encapsulation par le biais de conventions et de pratiques de programmation plutôt que par des modificateurs d'accès imposés. Vous voyez, le principe fondamental qui sous-tend une grande partie de la conception du code Python est "Nous sommes tous des adultes ici". Nous ne pouvons mettre en œuvre l'encapsulation que comme une simple convention et attendre des autres développeurs Python qu'ils fassent confiance à notre code et le respectent.
Dans d'autres langages OOP tels que Java et C++, l'encapsulation est strictement appliquée avec des modificateurs d'accès tels que public
, private
ou protected
, mais Python n'a pas ces modificateurs.
Ainsi, la plupart, sinon la totalité, des techniques d'encapsulation que je vais vous présenter sont des conventions de Python. Ils peuvent facilement être cassés si vous le décidez. Mais j'espère que vous les respecterez et les suivrez dans vos propres projets de développement.
Modificateurs d'accès en Python
Supposons que nous ayons cette classe simple :
class Tree:
def __init__(self, height):
self.height = height
pine = Tree(20)
print(pine.height)
20
Il possède un seul attribut de hauteur que nous pouvons imprimer. Le problème, c'est que nous pouvons aussi le modifier à notre guise :
pine.height = 50
pine.height
50
pine.height = "Grandma"
pine.height
'Grandma'
Alors, comment dire aux utilisateurs qu'il est interdit de changer de hauteur ? Eh bien, nous pourrions en faire un membre protégé en ajoutant un seul trait de soulignement :
class Tree:
def __init__(self, height):
self._height = height
pine = Tree(20)
pine._height
20
Les personnes qui connaissent cette convention savent qu'elles ne peuvent accéder qu'à l'attribut et que nous les décourageons fortement de l'utiliser et de le modifier. Mais s'ils le souhaitent, ils peuvent le modifier, oh oui.
Alors, comment éviter cela aussi ? En utilisant une autre convention, transformez l'attribut en un membre privé en ajoutant un double trait de soulignement :
class Tree:
def __init__(self, height):
self.__height = height
pine = Tree(20)
pine.__height
AttributeError: 'Tree' object has no attribute '__height'
Désormais, Python lèvera une erreur si quelqu'un tente d'accéder à l'attribut, et a fortiori de le modifier.
Mais avez-vous remarqué ce que nous venons de faire ? Nous cachons aux utilisateurs les seules informations relatives à nos objets. Notre classe est devenue inutile parce qu'elle n'a pas d'attributs publics.
Alors, comment exposer la hauteur des arbres aux utilisateurs tout en contrôlant la manière dont ils y accèdent et les modifient ? Par exemple, nous voulons que la hauteur des arbres se situe dans une fourchette spécifique et ne comporte que des valeurs entières. Comment faire respecter cette règle ?
À ce stade, votre ami qui utilise Java pourrait intervenir et vous suggérer d'utiliser les méthodes getter et setter. Essayons donc d'abord cela :
class Tree:
def __init__(self, height):
self.__height = height
def get_height(self):
return self.__height
def set_height(self, new_height):
if not isinstance(new_height, int):
raise TypeError("Tree height must be an integer")
if 0 < new_height <= 40:
self.__height = new_height
else:
raise ValueError("Invalid height for a pine tree")
pine = Tree(20)
pine.get_height()
20
De cette manière, vous créez un attribut privé __height
mais vous laissez les utilisateurs y accéder et le modifier de manière contrôlée à l'aide des méthodes get_height
et set_height
.
pine.set_height(25)
pine.get_height()
25
Avant de définir une nouvelle valeur, set_height
s'assure que la nouvelle hauteur se situe dans une certaine fourchette et qu'elle est numérique.
pine.set_height("Password")
TypeError: Tree height must be an integer
Mais ces méthodes semblent excessives pour une opération simple. En outre, il est moche d'écrire un tel code :
# Increase height by 5
pine.set_height(pine.get_height() + 5)
Ne serait-il pas plus beau et plus lisible si nous pouvions écrire ce code :
pine.height += 5
tout en respectant le type de données et l'intervalle corrects pour la hauteur ? La réponse est oui et nous allons apprendre à le faire dans la section suivante.
Utilisation du décorateur @property dans les classes Python
Nous introduisons une nouvelle technique : la création de propriétés pour les attributs :
class Tree:
def __init__(self, height):
# First, create a private or protected attribute
self.__height = height
@property
def height(self):
return self.__height
pine = Tree(17)
pine.height
17
Nous voulons que les utilisateurs accèdent à un attribut caché appelé __height
comme s'il s'agissait d'un attribut normal appelé height
. Pour ce faire, nous définissons une méthode nommée height
qui renvoie self.__height
et la décorons avec @property
.
Nous pouvons maintenant appeler height
et accéder à l'attribut privé :
pine.height
17
Mais le plus beau, c'est que les utilisateurs ne peuvent pas le modifier :
pine.height = 15
AttributeError: can't set attribute 'height'
Nous ajoutons donc une autre méthode appelée height(self, new_height)
qui est enveloppée par un décorateur height.setter
. Dans cette méthode, nous mettons en œuvre la logique qui impose le type de données et l'intervalle souhaités pour la hauteur :
class Tree:
def __init__(self, height):
self.__height = height
@property
def height(self):
return self.__height
@height.setter
def height(self, new_height):
if not isinstance(new_height, int):
raise TypeError("Tree height must be an integer")
if 0 < new_height <= 40:
self.__height = new_height
else:
raise ValueError("Invalid height for a pine tree")
Désormais, lorsqu'un utilisateur tente de modifier l'attribut height
, @height.setter
est appelé, ce qui permet de s'assurer que la valeur transmise est correcte :
pine = Tree(10)
pine.height = 33 # Calling @height.setter
pine.height = 45 # An error is raised
ValueError: Invalid height for a pine tree
Nous pouvons également personnaliser l'accès à l'attribut height
par le biais de la notation par points avec @height.getter
:
class Tree:
def __init__(self, height):
self.__height = height
@property
def height(self):
return self.__height
@height.getter
def height(self):
# You can return a custom version of height
return f"This tree is {self.__height} meters"
pine = Tree(33)
pine.height
'This tree is 33 meters'
Même si nous avons créé pine
avec une hauteur entière, nous pouvons modifier sa valeur avec @height.getter
.
Il s'agit là d'exemples de la manière dont nous pouvons promouvoir l'encapsulation dans une classe Python. N'oubliez pas que l'encapsulation reste une convention car nous pouvons toujours briser le membre privé interne __height
:
pine._Tree__height = "Gotcha!"
pine.height
'This tree is Gotcha! meters'
Dans les classes Python, tout est public, de même que les méthodes privées. Il ne s'agit pas d'un défaut de conception, mais d'un exemple de l'approche "Nous sommes tous des adultes ici".
Bonnes pratiques pour la mise en œuvre de l'encapsulation
Il existe un certain nombre de bonnes pratiques que vous pouvez suivre pour vous assurer que votre code s'aligne bien sur le code écrit par d'autres OOPistes expérimentés :
- Créez des attributs ou des méthodes protégés ou privés s'ils ne sont utilisés que par vous. Les membres protégés ou privés sont exclus de la documentation et signalent aux autres qu'ils peuvent être modifiés par vous, le développeur, sans préavis, ce qui les dissuade de les utiliser.
- Il n'est pas toujours nécessaire de créer des propriétés pour chaque attribut de classe. Pour les grandes classes comportant de nombreux attributs, l'écriture des méthodes
attr.getter
etattr.setter
peut devenir un véritable casse-tête. - Envisagez de déclencher un avertissement chaque fois qu'un utilisateur accède à un membre protégé (
_
). - Utilisez les membres privés avec parcimonie, car ils peuvent rendre le code illisible pour ceux qui ne connaissent pas la convention.
- Privilégiez la clarté à l'obscurité. Comme l'encapsulation vise à améliorer la maintenabilité du code et la protection des données, ne cachez pas complètement les détails importants de l'implémentation de votre classe.
- Si vous souhaitez créer des propriétés en lecture seule, ne mettez pas en œuvre la méthode
@attr.setter
. Les utilisateurs pourront accéder à la propriété mais pas la modifier. - Rappelez-vous toujours que l'encapsulation est une convention, et non un aspect obligatoire de la syntaxe Python.
- Pour les classes simples, envisagez d'utiliser des classes de données qui vous permettent d'encapsuler la classe avec une seule ligne de code. En revanche, les classes de données sont destinées à des classes plus simples dont les attributs et les méthodes sont prévisibles. Consultez ce tutoriel sur les classes de données pour en savoir plus.
Conclusion et autres ressources
Dans ce tutoriel, nous avons appris l'un des piliers fondamentaux de la programmation orientée objet en Python : l'encapsulation.
L'encapsulation vous permet de définir un accès contrôlé aux données stockées dans les objets de votre classe. Cela vous permet d'écrire un code propre, lisible et efficace et d'éviter toute modification ou suppression accidentelle des données de votre classe.
Voici d'autres ressources qui vous permettront d'approfondir vos connaissances en matière de POO :
Je suis un créateur de contenu en science des données avec plus de 2 ans d'expérience et l'un des plus grands followings sur Medium. J'aime écrire des articles détaillés sur l'IA et la ML dans un style un peu sarcastıc, car il faut bien faire quelque chose pour les rendre un peu moins ennuyeux. J'ai produit plus de 130 articles et un cours DataCamp, et un autre est en cours d'élaboration. Mon contenu a été vu par plus de 5 millions de personnes, dont 20 000 sont devenues des adeptes sur Medium et LinkedIn.
Poursuivez votre voyage en Python dès aujourd'hui !
cursus
Programmation en Python
cours