Menu
Coddy logo textTech

Attributi privati

Fa parte della sezione Programmazione orientata agli oggetti del percorso Python di Coddy. Lezione 15 di 64.

Gli attributi privati usano i trattini bassi per indicare che determinati dati non dovrebbero essere accessibili direttamente dall’esterno della classe.

Perché abbiamo bisogno degli attributi privati? Immagina di avere un conto bancario. Non vuoi che qualcuno modifichi direttamente il tuo saldo: dovrebbe seguire i canali appropriati (metodi di deposito/prelievo), così la banca può convalidare la transazione, verificare eventuali frodi, tenere traccia delle operazioni e così via. Gli attributi privati funzionano allo stesso modo: proteggono i tuoi dati da modifiche errate.

Ecco un esempio che usa un singolo trattino basso (convenzione per «uso interno»):

class Person:
    def __init__(self, name, age):
        self._name = name    # "protected" - uso interno
        self._age = age      # "protected" - uso interno

È comunque possibile accedere agli attributi con un singolo trattino basso, ma è un segnale che indica di non farlo:

person = Person("Alice", 30)
print(person._name)  # Funziona, ma non è consigliato

Usa i doppi underscore per una maggiore privacy (name mangling):

class Person:
    def __init__(self, name, age):
        self.__name = name   # "private" - il nome viene modificato automaticamente
        self.__age = age     # "private" - il nome viene modificato automaticamente
    
    def get_name(self):
        return self.__name
    
    def set_age(self, age):
        if age >= 0:
            self.__age = age
        else:
            print("Age must be positive!")

Perché usare i metodi getter e setter? Sono come dei guardiani. Invece di permettere a chiunque di modificare direttamente __age (il che potrebbe causare età negative o altri dati non validi), imponiamo di usare set_age(), che convalida prima l’input. Questo previene i bug e garantisce l’integrità dei dati.

Il «codice extra» ha uno scopo:

  • get_name() - Accesso controllato per leggere il nome
  • set_age(age) - Convalida l’età prima di consentire le modifiche (niente età negative!)
  • Senza questi metodi, qualcuno potrebbe impostare age = -100 e compromettere il tuo programma

Usa i metodi di accesso per interagire con gli attributi privati:

person = Person("Bob", 25)
print(person.get_name())  # Bob

person.set_age(30)        # Valido: age diventa 30
person.set_age(-5)        # Non valido: age deve essere positivo! (age rimane 30)

Vedi il vantaggio? Il metodo set_age() impedisce dati non validi. Senza di esso, potresti creare accidentalmente una persona con età -5, il che non ha senso!

Gli attributi con doppio trattino basso subiscono il «name mangling», ma è comunque possibile accedervi:

person = Person("Charlie", 35)
# Questo non funziona:
# print(person.__name)  # AttributeError

# Ma questo funziona (sconsigliato):
print(person._Person__name)  # Charlie

Esempio reale - Perché tutto questo codice?

class BankAccount:
    def __init__(self, balance):
        self.__balance = balance  # Private: can't be changed directly
    
    def deposit(self, amount):
        if amount > 0:
            self.__balance += amount
            return True
        return False
    
    def get_balance(self):
        return self.__balance

# Without private attributes:
# account.__balance = -1000000  # Disaster! Negative balance allowed

# With private attributes:
account = BankAccount(100)
account.deposit(50)           # Safe: validated
print(account.get_balance())  # 150

Il "codice extra" (metodi) protegge i tuoi dati da modifiche non valide. È come avere guardie di sicurezza invece di lasciare i tuoi oggetti di valore senza protezione.

Output:

Alice
Bob
Age must be positive!
Charlie
150

Un trattino basso singolo _attribute significa per convenzione «solo per uso interno». Il doppio trattino basso __attribute attiva la manipolazione dei nomi per una maggiore riservatezza. Usa i metodi getter/setter (metodi di accesso) per interagire correttamente con i dati privati e aggiungere la convalida. Il «codice extra» previene i bug assicurando che i dati siano sempre validi.

challenge icon

Sfida

Facile

Completa la classe User in user.py con la funzionalità di password privata, poi usala in driver.py. Segui i commenti TODO per implementare i metodi di verifica e modifica della password.

Provalo tu

# TODO: Importa la classe User dal modulo user

# TODO: Ottieni il caso di test dall'input
test_case = input()

if test_case == "constructor_test":
    # TODO: Crea un oggetto User con password iniziale "secure123"
    user = User("secure123")
    print("User created successfully")
    print("Initial password set")

elif test_case == "correct_password_test":
    # TODO: Crea un oggetto User con password iniziale "secure123"
    user = User("secure123")
    # TODO: Verifica il metodo check_password con la password corretta
    #       Stampa "Password check successful!" se restituisce True
    #       Stampa "Password check failed!" se restituisce False
    result = user.check_password("secure123")
    if result:
        print("Password check successful!")
    else:
        print("Password check failed!")

elif test_case == "incorrect_password_test":
    # TODO: Crea un oggetto User con password iniziale "secure123"
    user = User("secure123")
    # TODO: Verifica il metodo check_password con la password errata "wrong"
    #       Stampa "Incorrect password rejected!" se restituisce False
    #       Stampa "Security issue: incorrect password accepted!" se restituisce 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: Crea un oggetto User con password iniziale "secure123"
    user = User("secure123")
    # TODO: Verifica la modifica della password da "secure123" a "newpass456"
    #       Stampa "Password changed successfully!" se restituisce True
    #       Stampa "Password change failed!" se restituisce False
    result = user.change_password("secure123", "newpass456")
    if result:
        print("Password changed successfully!")
    else:
        print("Password change failed!")
    
    # TODO: Verifica che la nuova password funzioni controllando "newpass456"
    #       Stampa "New password works!" se restituisce True
    #       Stampa "New password doesn't work!" se restituisce 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: Crea un oggetto User con password iniziale "secure123"
    user = User("secure123")
    # TODO: Prova a cambiare la password usando una vecchia password errata
    #       Stampa "Security working: incorrect old password rejected!" se restituisce False
    #       Stampa "Security issue: password changed with wrong old password!" se restituisce 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: Crea un oggetto User con password iniziale "secure123"
    user = User("secure123")
    print("User created with initial password")
    
    # TODO: Verifica il metodo check_password con la password corretta
    if user.check_password("secure123"):
        print("Initial password verification successful")
    
    # TODO: Verifica il metodo check_password con una password errata
    if not user.check_password("wrongpass"):
        print("Incorrect password properly rejected")
    
    # TODO: Verifica la modifica della password da "secure123" a "newpass456"
    if user.change_password("secure123", "newpass456"):
        print("Password successfully changed")
    
    # TODO: Verifica che la vecchia password non funzioni più
    if not user.check_password("secure123"):
        print("Old password no longer works")
    
    # TODO: Verifica che la nuova password funzioni
    if user.check_password("newpass456"):
        print("New password works correctly")
    
    # TODO: Prova a cambiare la password usando una vecchia password errata
    if not user.change_password("wrongold", "hackerpw"):
        print("Security maintained: wrong old password rejected")
    
    # TODO: Verifica che la password sia ancora sicura dopo un tentativo di modifica non riuscito
    if user.check_password("newpass456"):
        print("Password remains secure after failed change attempt")

else:
    print("Unknown test case")
quiz iconMettiti alla prova

Questa lezione include un breve quiz. Inizia la lezione per rispondere e tenere traccia dei tuoi progressi.

Tutte le lezioni di Programmazione orientata agli oggetti

Esercitati da solo: Compilatore Python online