Le guichet de la banque
Vous ne rentrez pas dans le coffre-fort pour prendre votre argent. Vous passez par le guichet, qui vérifie votre identité et votre solde. Le coffre, c'est l'attribut privé ; le guichet, ce sont les méthodes.
Cacher les attributs sensibles d'un objet et n'y donner accès qu'à travers des méthodes (accesseurs et modificateurs) qui vérifient les règles.
Dans l'application d'une banque, n'importe quelle partie du programme peut écrire compte.solde = -5000. Résultat : des soldes négatifs, des dépôts de zéro dirham, et personne ne sait qui a modifié quoi. L'encapsulation règle ce problème : le solde devient privé, et on ne le change qu'en passant par deposer() et retirer(), qui vérifient les montants.
L'objet compte cache son solde dans une zone privée (__solde). De l'extérieur, on ne voit que ses méthodes.
L’encapsulation consiste à cacher l’intérieur d’un objet et à n’exposer que ce qui est utile. On décide qui peut lire et modifier chaque attribut.
| Nom | Visibilité | Accès depuis l’extérieur |
|---|---|---|
titulaire | public | libre |
_titulaire | protégé (convention) | possible, mais déconseillé |
__solde | privé | bloqué : Python renomme l’attribut |
Un attribut privé n’est accessible qu’à l’intérieur des méthodes de la classe.
Pour lire ou modifier un attribut privé, on écrit des méthodes :
get_solde() ;set_solde(valeur).En Python, la forme la plus élégante est @property : on écrit compte.solde comme un attribut, mais c’est une méthode qui s’exécute.
@property
def solde(self):
return self.__solde
Sans setter, l’attribut est en lecture seule : compte.solde = 5 déclenche une erreur.
Vous ne rentrez pas dans le coffre-fort pour prendre votre argent. Vous passez par le guichet, qui vérifie votre identité et votre solde. Le coffre, c'est l'attribut privé ; le guichet, ce sont les méthodes.
On ne règle pas la chaudière à la main : on passe par le thermostat, qui refuse les valeurs absurdes. Un modificateur (setter) fait pareil.
def set_temperature(self, t): if 5 <= t <= 30: self.__temperature = t else: print("Valeur refusée")
Exécutez le code : les objets et leurs attributs s'afficheront ici.
Écrivez une classe Personne avec un attribut privé __age, un getter get_age() et un setter set_age(age) qui refuse un âge négatif ou supérieur à 120.
class Personne: def __init__(self, nom, age): self.nom = nom self.__age = 0 self.set_age(age) def get_age(self): return self.__age def set_age(self, age): if 0 <= age <= 120: self.__age = age else: print("Âge invalide") p = Personne("Omar", 30) p.set_age(-4) # Âge invalide print(p.get_age()) # 30
Réécrivez la classe Personne en utilisant @property et @age.setter, pour pouvoir écrire p.age = 31.
Le setter se déclare avec @age.setter juste au-dessus d'une méthode qui porte le même nom age.
class Personne: def __init__(self, nom, age): self.nom = nom self.age = age # passe par le setter @property def age(self): return self.__age @age.setter def age(self, valeur): if not 0 <= valeur <= 120: raise ValueError("Âge invalide") self.__age = valeur p = Personne("Omar", 30) p.age = 31 print(p.age)
Que se passe-t-il si vous écrivez print(compte.__solde) en dehors de la classe ? Et print(compte._CompteBancaire__solde) ? Expliquez.
compte.__solde provoque une AttributeError : Python a renommé l'attribut en _CompteBancaire__solde (name mangling). compte._CompteBancaire__solde fonctionne, mais c'est un contournement à ne jamais utiliser : en Python, le privé est une protection contre les erreurs, pas un verrou de sécurité.