<aside> <img src="notion://custom_emoji/899aa73c-260f-813c-baa9-0003a270e9e6/29eaa73c-260f-8057-931f-007a0ac7402b" alt="notion://custom_emoji/899aa73c-260f-813c-baa9-0003a270e9e6/29eaa73c-260f-8057-931f-007a0ac7402b" width="40px" />

Indice

</aside>

Metodi privati

Nella programmazione ad oggetti non tutto ciò che una classe sa fare deve essere per forza visibile all’esterno.

Un metodo privato serve a nascondere i dettagli interni e rendere chiaro cosa una classe fa, non come lo fa, in continuità con la filosofia dell’incapsulamento.

In Python, per convenzione, un metodo privato si scrive con due underscore iniziali: def __nome_metodo(self)

class Player:
    def __init__(self, name):
        self.__name = name
        self.__health = 50    
    
    def take_damage(self, amount):
        self.__apply_damage(amount)   # metodo privato interno
        print(f"{self.__name} ha {self.__health} HP.")
        
    def __apply_damage(self, amount):
        if amount < 0:
            return
        self.__health -= amount
        if self.__health < 0:
            self.__health = 0

In questo esempio, __apply_damage() è un dettaglio interno: solo Player deve usarlo.

Chi usa la classe potrà chiamare take_damage(), non __apply_damage().

<aside> 💡

Quando creare un metodo privato?

Quando vuoi suddividere il codice in piccoli passi leggibili, ma non vuoi che chi usa la classe possa richiamare quei passi direttamente. Non avrebbe senso per come è pensato il flusso di esecuzione dei metodi e rischierebbe di rompere lo stato interno dell’oggetto.

</aside>


Property e setter

Nell’esercizio Parte 2: incapsulamento e visibilità abbiamo scritto getter e setter come metodi normali, ad esempio:

def get_health(self):
    return self.__health
    
def set_health(self, value):
    if value >= 0:
        self.__health = value

In Python esiste un modo più elegante e “pythonic”: usare i decoratori @property e @nome.setter.

class Player:
    def __init__(self, name, max_health):
        self.__name = name
        self.__max_health = max_health
        self.__health = max_health
        
    @property
    def health(self):
        return self.__health
    
    @health.setter
    def health(self, value):
        # blocca valori fuori dai limiti
        if value < 0:
            self.__health = 0
        elif value > self.__max_health:
            self.__health = self.__max_health
        else:
            self.__health = value

Ora, dall’esterno si può accedere all’attributo health come se fosse una variabile:

gimli = Player("Gimli", 50)
gimli.health -= 10    # chiama automaticamente il setter
print(gimli.health)   # chiama automaticamente il getter

Questa sintassi mantiene la sicurezza dei dati ma è molto più chiara da leggere.

<aside> 💻

Cos’è un decoratore?

Un decoratore in Python è una funzione speciale che modifica o estende il comportamento di un’altra funzione o metodo senza cambiarne il codice interno.

Si applica anteponendo il simbolo @ al nome del decoratore, subito sopra la definizione della funzione o del metodo:

@decoratore
def mia_funzione():
    ...

Nel caso delle property, i decoratori @property, @nome.setter e @nome.deleter (che non abbiamo trattato) sono usati per trasformare metodi normali in attributi gestiti automaticamente, permettendo di aggiungere logica di controllo o validazione quando un valore viene letto, modificato o cancellato.

</aside>