CodeGym /Corsi /Python SELF IT /Metodi e campi speciali

Metodi e campi speciali

Python SELF IT
Livello 15 , Lezione 5
Disponibile

5.1 Livelli di accesso

Avrai probabilmente notato il nome strano del costruttore __init__? In futuro lo incontrerai abbastanza spesso.

In Python esistono diversi livelli di accesso agli attributi e metodi delle classi, che aiutano a controllare la visibilità e la sicurezza dei dati. I principali meccanismi di gestione dell'accesso includono l'uso di uno o due underscore (_ e __) davanti al nome dell'attributo o del metodo.

Utilizzo delle convenzioni

Un underscore _ viene usato per attributi e metodi che non dovrebbero essere usati all'esterno della classe o del modulo. Non è proibito, ma è una convenzione che va rispettata per migliorare la leggibilità e la manutenzione del codice.

Due underscore __ vengono usati per attributi e metodi che devono essere veramente privati e protetti da accessi accidentali o intenzionali esterni. Il meccanismo di name mangling li rende meno accessibili, ma comunque accessibili tramite nomi speciali.

Campi e metodi pubblici (public)

Attributi e metodi pubblici sono accessibili da qualsiasi parte del codice. In Python di default tutti gli attributi e i metodi sono pubblici, se i loro nomi non iniziano con un underscore.


class MyClass:
    def __init__(self):
        self.public_attribute = "I am public"
        
    def public_method(self):
        return "This is a public method"
        

obj = MyClass()
print(obj.public_attribute)  # Accessibile
print(obj.public_method())  # Accessibile

Tramite i livelli di accesso nel linguaggio Python si realizza l'incapsulamento, attraverso campi e metodi non pubblici.

5.2 Campi e metodi non pubblici

Campi e metodi protetti (protected)

Attributi e metodi protetti sono indicati con un underscore _ davanti al nome e sono destinati all'uso interno nella classe e nelle sue sottoclassi. È una convenzione che indica ai programmatori che i dati non sono destinati a essere usati al di fuori della classe.


class MyClass:
    def __init__(self):
        self._protected_attribute = "I am protected"
        
    def _protected_method(self):
        return "This is a protected method"
        

obj = MyClass()
print(obj._protected_attribute)  # Accessibile, ma non raccomandato
print(obj._protected_method())  # Accessibile, ma non raccomandato

Campi e metodi privati (private)

In Python gli attributi e i metodi privati sono indicati con due underscore __ davanti al nome. Questi attributi e metodi sono destinati all'uso esclusivo all'interno della classe e il loro principale obiettivo è nascondere l'implementazione interna e proteggere i dati da modifiche o usi accidentali dall'esterno.

Per prevenire l'accesso diretto a tali attributi e metodi da codice esterno, Python applica un meccanismo speciale noto come name mangling (offuscamento dei nomi). Questo meccanismo cambia automaticamente i nomi degli attributi privati, aggiungendo un prefisso composto dal nome della classe. Così, un attributo privato __private_attribute nella classe MyClass viene trasformato nel nome interno _MyClass__private_attribute.

Questo permette di proteggere i dati da accessi non intenzionali, mantenendo però la possibilità di lavorare con essi all'interno della classe. È importante ricordare, tuttavia, che il meccanismo di "name mangling" non è una protezione assoluta — un programmatore esperto può accedere a questi dati usando il nome modificato.

Vediamo ora come funziona in pratica:


class MyClass:
    def __init__(self):
        self.__private_attribute = "I am private"
        
    def __private_method(self):
        return "This is a private method"
        
    def access_private_method(self):
        return self.__private_method()
         

obj = MyClass()
# print(obj.__private_attribute)  # Errore, non accessibile direttamente
# print(obj.__private_method())  # Errore, non accessibile direttamente
print(obj.access_private_method())  # Accessibile tramite metodo pubblico della classe

Come puoi vedere dall'esempio, l'accesso diretto agli attributi o ai metodi privati genera un errore. Ma Python mantiene la possibilità di accedervi attraverso il nome modificato. Ad esempio, puoi accedere all'attributo privato utilizzando il nome "offuscato", come mostrato qui sotto:


class MyClass:
    def __init__(self):
        self.__private_attribute = "I am private"
   
obj = MyClass()
print(obj._MyClass__private_attribute)  # Stampa: I am private

Anche se l'accesso tramite nome "offuscato" è possibile, dovrebbe essere evitato, poiché viola i principi dell'incapsulamento e può portare a instabilità nel codice.

Per vedere come Python modifica i nomi degli attributi, puoi usare la funzione incorporata dir(), che mostra tutti gli attributi e metodi disponibili di un oggetto:


class MyClass:
    def __init__(self):
        self.__private_attribute = "I am private"
   
obj = MyClass()
print(dir(obj))  # Stampa tutti gli attributi e metodi dell'oggetto, inclusi i nomi "offuscati"

L'esecuzione della funzione dir() mostrerà un elenco di tutti gli attributi e metodi dell'oggetto, incluso _MyClass__private_attribute, confermando il meccanismo di "name mangling".

5.3 Invocazione automatica dei metodi

C'era un aspetto interessante nel lavorare con i costruttori, su cui potresti aver prestato attenzione. Il metodo __init__ veniva invocato automaticamente.

Ci sono molte situazioni del genere, così come metodi per queste situazioni. Esempi:

Metodo __str__

Se il tuo oggetto ha un metodo __str__, questo verrà invocato automaticamente quando si tenta di convertire l'oggetto in una stringa, ad esempio, quando si usano le funzioni print() e str().


class Cat:
    def __init__(self, name, age):
        self.name = name
        self.age = age
            
    def __str__(self):
        return f"{self.name} è {self.age} anni"
            

cat = Cat("Barsik", 5)
print(cat)  # Stampa: Barsik è 5 anni

Metodo __len__

E se il tuo oggetto ha un metodo __len__, verrà invocato automaticamente quando si tenta di determinare la "lunghezza" del tuo oggetto — usato dalla funzione len(). Esempio:


class MyList:
    def __init__(self, items):
        self.items = items
        
    def __len__(self):
        return len(self.items)
        
        
my_list = MyList([1, 2, 3])
print(len(my_list))  # Stampa: 3

Di "metodi speciali" del genere ne incontrerai tanti nella tua vita, ma lavorarci è un piacere. Quindi preparati :)

Commenti
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION