Menu
Coddy logo textTech

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 interne

Les 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 nom
  • set_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 = -100 et 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)  # Charlie

Exemple 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())  # 150

Le « 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
150

Un 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.

challenge icon

Défi

Facile

Complé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 interne

Double 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)  # Charlie

Essayez 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")
quiz iconTestez-vous

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