Attributs privés
Fait partie de la section Object Oriented Programming du Journey Python de Coddy — leçon 15 sur 64.
Les attributs privés utilisent des tirets bas pour indiquer que certaines données ne doivent pas être consultées directement depuis l'extérieur de la classe.
Pourquoi avons-nous besoin d'attributs privés ? Imaginez que vous ayez un compte bancaire. Vous ne voulez pas que n'importe qui puisse modifier directement votre solde - ils doivent passer par les canaux appropriés (méthodes de dépôt/retrait) afin que la banque puisse valider la transaction, vérifier les fraudes, tenir des registres, etc. Les attributs privés fonctionnent de la même manière - ils protègent vos données contre toute modification incorrecte.
Voici un exemple utilisant un simple souligné (convention pour un « usage interne ») :
class Person:
def __init__(self, name, age):
self._name = name # « protégé » - usage interne
self._age = age # « protégé » - usage interneLes attributs avec un seul souligné peuvent toujours être accédés, mais c'est un signal pour ne pas le faire :
person = Person("Alice", 30)
print(person._name) # Fonctionne, mais n'est pas recommandéUtilisez des doubles underscores pour une confidentialité accrue (name mangling) :
class Person:
def __init__(self, name, age):
self.__name = name # "privé" - subit le name mangling
self.__age = age # "privé" - subit le name mangling
def get_name(self):
return self.__name
def set_age(self, age):
if age >= 0:
self.__age = age
else:
print("Age must be positive!")Pourquoi utiliser des méthodes getter et setter ? Elles agissent comme des gardiens. Au lieu de laisser n'importe qui modifier __age directement (ce qui pourrait entraîner des âges négatifs ou d'autres données invalides), nous les obligeons à utiliser set_age() qui valide d'abord l'entrée. Cela évite les bugs et garantit l'intégrité des données.
Le « code supplémentaire » a un but :
get_name()- Accès contrôlé pour lire le nomset_age(age)- Valide l'âge avant d'autoriser les modifications (pas d'âges négatifs !)- Sans ces méthodes, quelqu'un pourrait définir
age = -100et faire planter votre programme
Utilisez les méthodes d'accès pour interagir avec les attributs privés :
person = Person("Bob", 25)
print(person.get_name()) # Bob
person.set_age(30) # Valide : l'âge devient 30
person.set_age(-5) # Invalide : L'âge doit être positif ! (l'âge reste 30)Vous voyez l'avantage ? La méthode set_age() empêche les données invalides. Sans elle, vous pourriez accidentellement créer une personne avec un âge de -5, ce qui n'a aucun sens !
Les attributs à double underscore subissent un « name mangling » (mutilation de nom) mais peuvent toujours être consultés :
person = Person("Charlie", 35)
# Cela ne fonctionne pas :
# print(person.__name) # AttributeError
# Mais cela fonctionne (déconseillé) :
print(person._Person__name) # CharlieExemple concret - Pourquoi tout ce code ?
class BankAccount:
def __init__(self, balance):
self.__balance = balance # Privé : ne peut pas être modifié directement
def deposit(self, amount):
if amount > 0:
self.__balance += amount
return True
return False
def get_balance(self):
return self.__balance
# Sans attributs privés :
# account.__balance = -1000000 # Désastre ! Solde négatif autorisé
# Avec des attributs privés :
account = BankAccount(100)
account.deposit(50) # Sûr : validé
print(account.get_balance()) # 150Le « code supplémentaire » (méthodes) protège vos données contre les modifications invalides. C'est comme avoir des agents de sécurité au lieu de laisser vos objets de valeur sans protection.
Sortie :
Alice
Bob
Age must be positive!
Charlie
150Un simple tiret bas _attribute signifie « usage interne uniquement » par convention. Le double tiret bas __attribute déclenche le « name mangling » pour une confidentialité accrue. Utilisez des méthodes getter/setter (méthodes d'accès) pour interagir correctement avec les données privées et ajouter une validation. Le « code supplémentaire » prévient les bogues en garantissant que les données sont toujours valides.
Défi
FacileComplétez la classe User dans user.py avec une fonctionnalité de mot de passe privé, puis utilisez-la dans driver.py. Suivez les commentaires TODO pour implémenter les méthodes de vérification et de changement de mot de passe.
Aide-mémoire
Les attributs privés utilisent des tirets bas pour indiquer que les données ne doivent pas être accédées directement depuis l'extérieur de la classe.
Tiret bas simple (_attribute) - convention pour un « usage interne » :
class Person:
def __init__(self, name, age):
self._name = name # "protected" - usage interne
self._age = age # "protected" - usage interneDouble tiret bas (__attribute) - déclenche le « name mangling » (mutilation de nom) pour une confidentialité accrue :
class Person:
def __init__(self, name, age):
self.__name = name # "private" - subit le name mangling
self.__age = age # "private" - subit le name mangling
def get_name(self):
return self.__name
def set_age(self, age):
if age >= 0:
self.__age = age
else:
print("Age must be positive!")Utilisez des méthodes d'accès pour interagir avec les attributs privés :
person = Person("Bob", 25)
print(person.get_name()) # Bob
person.set_age(30)
person.set_age(-5) # Age must be positive!Les attributs avec un double tiret bas subissent le name mangling mais peuvent toujours être accédés (déconseillé) :
# Ceci ne fonctionne pas :
# print(person.__name) # AttributeError
# Mais ceci fonctionne (déconseillé) :
print(person._Person__name) # CharlieEssayez vous-même
# TODO: Importer la classe User depuis le module user
# TODO: Obtenir le cas de test à partir de l'entrée
test_case = input()
if test_case == "constructor_test":
# TODO: Créer un objet User avec le mot de passe initial "secure123"
user = User("secure123")
print("User created successfully")
print("Initial password set")
elif test_case == "correct_password_test":
# TODO: Créer un objet User avec le mot de passe initial "secure123"
user = User("secure123")
# TODO: Tester la méthode check_password avec le mot de passe correct
# Afficher "Password check successful!" si elle retourne True
# Afficher "Password check failed!" si elle retourne False
result = user.check_password("secure123")
if result:
print("Password check successful!")
else:
print("Password check failed!")
elif test_case == "incorrect_password_test":
# TODO: Créer un objet User avec le mot de passe initial "secure123"
user = User("secure123")
# TODO: Tester la méthode check_password avec le mot de passe incorrect "wrong"
# Afficher "Incorrect password rejected!" si elle retourne False
# Afficher "Security issue: incorrect password accepted!" si elle retourne True
result = user.check_password("wrong")
if not result:
print("Incorrect password rejected!")
else:
print("Security issue: incorrect password accepted!")
elif test_case == "change_password_test":
# TODO: Créer un objet User avec le mot de passe initial "secure123"
user = User("secure123")
# TODO: Tester le changement de mot de passe de "secure123" à "newpass456"
# Afficher "Password changed successfully!" si elle retourne True
# Afficher "Password change failed!" si elle retourne False
result = user.change_password("secure123", "newpass456")
if result:
print("Password changed successfully!")
else:
print("Password change failed!")
# TODO: Vérifier que le nouveau mot de passe fonctionne en testant "newpass456"
# Afficher "New password works!" si elle retourne True
# Afficher "New password doesn't work!" si elle retourne False
if user.check_password("newpass456"):
print("New password works!")
else:
print("New password doesn't work!")
elif test_case == "change_password_wrong_old_test":
# TODO: Créer un objet User avec le mot de passe initial "secure123"
user = User("secure123")
# TODO: Essayer de changer le mot de passe avec un ancien mot de passe incorrect
# Afficher "Security working: incorrect old password rejected!" si elle retourne False
# Afficher "Security issue: password changed with wrong old password!" si elle retourne True
result = user.change_password("wrong", "hackerpw")
if not result:
print("Security working: incorrect old password rejected!")
else:
print("Security issue: password changed with wrong old password!")
elif test_case == "comprehensive_test":
# TODO: Créer un objet User avec le mot de passe initial "secure123"
user = User("secure123")
print("User created with initial password")
# TODO: Tester la méthode check_password avec le mot de passe correct
if user.check_password("secure123"):
print("Initial password verification successful")
# TODO: Tester la méthode check_password avec un mot de passe incorrect
if not user.check_password("wrongpass"):
print("Incorrect password properly rejected")
# TODO: Tester le changement de mot de passe de "secure123" à "newpass456"
if user.change_password("secure123", "newpass456"):
print("Password successfully changed")
# TODO: Vérifier que l'ancien mot de passe ne fonctionne plus
if not user.check_password("secure123"):
print("Old password no longer works")
# TODO: Vérifier que le nouveau mot de passe fonctionne
if user.check_password("newpass456"):
print("New password works correctly")
# TODO: Essayer de changer le mot de passe avec un ancien mot de passe incorrect
if not user.change_password("wrongold", "hackerpw"):
print("Security maintained: wrong old password rejected")
# TODO: Vérifier que le mot de passe est toujours sécurisé après une tentative de changement échouée
if user.check_password("newpass456"):
print("Password remains secure after failed change attempt")
else:
print("Unknown test case")Cette leçon comprend un petit quiz. Commencez la leçon pour y répondre et suivre votre progression.
Toutes les leçons de Object Oriented Programming
1Les fondamentaux de la POO
Fichiers externesIntroduction à la POOClasses vs ObjetsLe paramètre selfMéthodesAttributsMéthode constructeur (__init__)Récapitulatif - Calculatrice simple4Héritage
Héritage de baseLa fonction super()Redéfinition de méthodeHéritage multipleOrdre de résolution des méthodesRécapitulatif - Hiérarchie des employés7Méthodes spéciales
Introduction aux méthodes magiquesSurcharge d'opérateursMéthodes magiques de conteneurRécapitulatif - Liste personnalisée10Patrons de conception, Partie 1
Introduction aux patrons de conceptionPatron SingletonPatron FactoryPatron ObserverPatron Strategy2Décorateurs
Introduction aux décorateursDécorateur de propriétéDécorateur de méthode statiqueDécorateur de méthode de classe5Polymorphisme
Retour sur la redéfinition de méthodeDuck TypingClasses et méthodes abstraitesConception d'interfacesRécapitulatif - Calculateur de formes8Concepts avancés de la POO
Composition vs HéritageMixinsMéthodes statiques et de classeDécorateurs de classeGestionnaires de contexte11Patrons de conception, Partie 2
Patron CommandePatron AdaptateurPatron DécorateurPatron Méthode TemplatePatron ÉtatPatron Composite3Propriétés de classe
Variables d'instance vs variables de classeDécorateurs de propriétéAttributs privésRécapitulatif - Gestionnaire de compte bancaire