Accéder au contenu principal

Encapsulation en Python : Un guide complet

Apprenez les principes fondamentaux de la mise en œuvre de l'encapsulation dans la programmation orientée objet de Python.
Actualisé 12 déc. 2024  · 11 min de lecture

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 :

Fonctionnalité Python Java C++
Modificateurs d'accès Aucun modificateur imposé ; utilisation des conventions de dénomination (_protected__private) Mise en œuvre avec publicprotectedprivate keywords Mise en œuvre avec publicprotectedprivate keywords
Méthodes Getter/Setter Facultatif, souvent utilisé avec @property décorateur pour l'accès contrôlé Pratique courante, généralement mise en œuvre sous forme de méthodes Pratique courante, généralement mise en œuvre sous forme de méthodes
Accès aux données Accessible via les conventions de dénomination ; dépend de la discipline du développeur Contrôlé par des modificateurs d'accès ; appliqué par le compilateur Contrôlé par des modificateurs d'accès ; appliqué par le compilateur
Philosophie "Nous sommes tous des adultes ici" - repose sur les conventions et la confiance Application stricte du contrôle d'accès Application stricte du contrôle d'accès

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 d'é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".

Voici un aperçu des modificateurs d'accès Python que nous venons de passer en revue :

Modifier/Convention Description Exemple
Public Niveau d'accès par défaut ; les attributs et les méthodes sont accessibles depuis l'extérieur de la classe self.attribute
Protégé Indiqué par un seul trait de soulignement ; une convention pour indiquer un accès partiellement restreint. self._attribute
Privé Indiqué par un double soulignement ; l'altération du nom permet une restriction d'accès limitée. self.__attribute
Propriétés Fournit un accès contrôlé aux attributs privés à l'aide de la fonction @property decorator @property def attribute(self):
Propriétés en lecture seule Non @attribute.setter; permet l'accès en lecture seule à un attribut @property def attribute(self):

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 :

  1. 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.
  2. 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 et attr.setter peut devenir un véritable casse-tête.
  3. Envisagez de déclencher un avertissement chaque fois qu'un utilisateur accède à un membre protégé (_).
  4. Utilisez les membres privés avec parcimonie, car ils peuvent rendre le code illisible pour ceux qui ne connaissent pas la convention.
  5. 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.
  6. 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.
  7. Rappelez-vous toujours que l'encapsulation est une convention, et non un aspect obligatoire de la syntaxe Python.
  8. 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 :

Apprenez Python à partir de zéro

Maîtrisez Python pour la science des données et acquérez des compétences recherchées.
Commencez à apprendre gratuitement

FAQ

En quoi l'encapsulation diffère-t-elle de l'abstraction en Python ?

L'encapsulation consiste à regrouper les données et les méthodes qui opèrent sur les données au sein d'une unité, telle qu'une classe, et à en restreindre l'accès à certains composants. L'abstraction, quant à elle, consiste à dissimuler la réalité complexe en n'en exposant que les parties essentielles. Alors que l'encapsulation se concentre sur la restriction de l'accès, l'abstraction se concentre sur la simplification des interactions.

L'encapsulation peut-elle être utilisée dans le cadre de la programmation fonctionnelle ou est-elle réservée à la POO ?

L'encapsulation est un concept étroitement lié à la programmation orientée objet (POO) et n'est généralement pas utilisée dans la programmation fonctionnelle. La programmation fonctionnelle met l'accent sur l'immuabilité et les fonctions sans état, contrairement à la POO, qui se concentre sur les objets et l'encapsulation des données dans ces objets.

Quels sont les écueils à éviter lors de la mise en œuvre de l'encapsulation en Python ?

Les pièges les plus courants sont l'utilisation excessive de variables privées, qui peut rendre le code difficile à maintenir, et le fait de ne pas fournir de méthodes d'accès adéquates (getters/setters), ce qui peut conduire à un code fragile. En outre, les développeurs peuvent oublier que l'encapsulation de Python se fait par convention et qu'elle peut donc être contournée.

Comment l'encapsulation peut-elle améliorer la sécurité du code ?

L'encapsulation peut améliorer la sécurité du code en limitant l'accès à l'état interne d'un objet et en empêchant les modifications non autorisées. En contrôlant la manière dont les données sont consultées et modifiées, les développeurs peuvent s'assurer que les données restent valides et cohérentes.

Comment l'utilisation du décorateur @property affecte-t-elle les performances en Python ?

L'utilisation du décorateur@property peut entraîner une légère surcharge due aux appels de méthodes supplémentaires, mais dans la plupart des cas, cet impact est négligeable. Les avantages liés à l'amélioration de la lisibilité et de la maintenabilité du code l'emportent souvent sur les coûts de performance mineurs.

Est-il possible d'appliquer strictement l'encapsulation en Python comme en Java ou en C++ ?

Python n'applique pas l'encapsulation de manière stricte comme Java ou C++. Sa philosophie repose sur des conventions et sur l'idée que les promoteurs respecteront l'utilisation prévue des membres privés et protégés. Toutefois, l'utilisation de conventions de dénomination telles que les traits de soulignement peut décourager les abus.

Quelles sont les applications concrètes de l'encapsulation dans les projets Python ?

L'encapsulation est utilisée pour protéger les données sensibles dans des applications telles que les logiciels bancaires, où les détails du compte sont cachés. Il est également utilisé dans les bibliothèques et les cadres de travail pour masquer les détails complexes de la mise en œuvre, ce qui permet aux développeurs d'interagir avec des interfaces simples.

Comment puis-je tester efficacement du code encapsulé en Python ?

Tester un code encapsulé implique d'écrire des tests unitaires pour l'interface publique d'une classe. En testant les méthodes et les propriétés exposées à l'utilisateur, vous vous assurez que les changements d'état internes sont correctement gérés et que la logique d'encapsulation est respectée.

L'encapsulation peut-elle être appliquée aux modules Python ou est-elle limitée aux classes ?

Bien que l'encapsulation soit principalement associée aux classes, le concept peut être étendu aux modules en utilisant des fonctions ou des variables préfixées par un trait de soulignement pour signaler un usage privé au sein d'un module.

Quel est le lien entre l'encapsulation et le polymorphisme en Python ?

L'encapsulation et le polymorphisme sont tous deux des principes de la POO, mais ils servent des objectifs différents. L'encapsulation restreint l'accès à certaines parties d'un objet, tandis que le polymorphisme permet aux objets d'être traités comme des instances de leur classe mère, ce qui facilite l'extension et la maintenance du code. Ils peuvent être utilisés ensemble pour créer des structures de code flexibles et sûres.


Bex Tuychiev's photo
Author
Bex Tuychiev
LinkedIn

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. 

Sujets

Poursuivez votre voyage en Python dès aujourd'hui !

Cursus

Programmation Python

0 min
Améliorez vos compétences en programmation. Apprenez à optimiser le code, à écrire des fonctions et des tests, et à utiliser les meilleures techniques de génie logiciel.
Afficher les détailsRight Arrow
Commencer le cours
Voir plusRight Arrow
Apparenté

blog

2022-2023 Rapport annuel DataCamp Classrooms

À l'aube de la nouvelle année scolaire, DataCamp Classrooms est plus motivé que jamais pour démocratiser l'apprentissage des données, avec plus de 7 650 nouveaux Classrooms ajoutés au cours des 12 derniers mois.
Nathaniel Taylor-Leach's photo

Nathaniel Taylor-Leach

8 min

blog

Les 20 meilleures questions d'entretien pour les flocons de neige, à tous les niveaux

Vous êtes actuellement à la recherche d'un emploi qui utilise Snowflake ? Préparez-vous à répondre à ces 20 questions d'entretien sur le flocon de neige pour décrocher le poste !
Nisha Arya Ahmed's photo

Nisha Arya Ahmed

15 min

blog

Q2 2023 DataCamp Donates Digest

DataCamp Donates a offert plus de 20k bourses d'études à nos partenaires à but non lucratif au deuxième trimestre 2023. Découvrez comment des apprenants défavorisés et assidus ont transformé ces opportunités en réussites professionnelles qui ont changé leur vie.
Nathaniel Taylor-Leach's photo

Nathaniel Taylor-Leach

blog

Célébration de Saghar Hazinyar : Une boursière de DataCamp Donates et une diplômée de Code to Inspire

Découvrez le parcours inspirant de Saghar Hazinyar, diplômée de Code to Inspire, qui a surmonté les défis en Afghanistan et s'est épanouie grâce à une bourse de DataCamp Donates.
Fereshteh Forough's photo

Fereshteh Forough

4 min

blog

Nous avons fait don de bourses DataCamp Premium à un million de personnes, et ce n'est pas fini.

Réparties entre nos deux programmes d'impact social, DataCamp Classrooms et #DCDonates, les bourses offrent un accès illimité à tout ce que DataCamp Premium a à offrir.
Nathaniel Taylor-Leach's photo

Nathaniel Taylor-Leach

Voir plusVoir plus