Classi in Python: istanze, metodi, ereditarietà

Classi in Python: istanze, metodi, ereditarietà

12 Set 2018 3 Di Admin M.

In questo articolo dedicato alle classi, metteremo insieme alcuni concetti visti finora (i tipi di dati, la sintassi e le funzioni ) per imparare a creare nuove classi in Python.

 

Le classi e le istanze

 

Questa volta partiamo subito con un esempio:

 

Classi in Python

 

Ecco fatto! Abbiamo definito la nostra prima classe. Troppo facile, vero?

L’unica novità è l’istruzione class, mentre l’istruzione pass  è lì solo perché dobbiamo per forza scrivere qualcosa nel corpo della definizione della nostra classe.

 

Ora proviamo a creare un’istanza della nostra classe:

 

Istanza della classe

 

Basta scrivere il nome della classe seguito dalla coppia di parentesi per far sì che Python ci fornisca un oggetto di quella classe, chiamato anche istanza  della classe.

 

Ora, se proviamo a digitare Cibo e poi pasta, otteniamo questi due risultati:

 

Valori in memoria della classe e della istanza

 

 


I valori possono naturalmente essere differenti: indicano la posizione in memoria in cui sono collocati gli oggetti visualizzati.


 

  • Nel caso di Cibo la parola class, presente della stringa visualizzata in output, indica che siamo in presenza di una classe.

 

  • Nel secondo caso invece, pasta, la stringa indica:

– sia il fatto che si tratta di un oggetto e quindi object;

– sia la classe che ne ha permesso la creazione, ovvero __main__.Cibo.

 

 


Il nome speciale __main__ indica che la classe Cibo è stata definita interattivamente.

Se invece fosse stata definita e impostata da un modulo esterno, al posto di __main__ vedremmo il nome del modulo in questione.


 

I metodi

 

Adesso che sappiamo come definire classi in Python e come creare un oggetto appartenente ad una classe, cosa possiamo fare?

Cominciamo ad applicare quanto imparato nei precedenti articoli.

 

Vediamo una versione un po’ più evoluta della nostra classe Cibo:

 

Esempio classi in Python: classe Cibo

 

 

La prima stringa che abbiamo aggiunto, dovremmo ricordarcelo, è la stringa di documentazione. 

Come sappiamo, viene automaticamente assegnata all’attributo __doc__ della classe e visualizzata da help.

 

Stringa di documentazione nella funzione help - Classe Cibo

 

 

Il metodo __init__ (attenzione: sono tue “_” consecutivi) può sembrare un po’ criptico: si tratta del metodo costruttore  per le classi in Python.

Quando creiamo un’istanza di una classe, è normale voler specificare alcune caratteristiche speciali e proprie dell’oggetto che stiamo costruendo.

Per fare ciò, possiamo definire un metodo apposito dal nome prestabilito __init__ (il costruttore appunto) e passargli dei valori all’atto della creazione dell’oggetto.

 

 

  • Il primo parametro del costruttore (e di tutti i metodi di una classe) è self, il quale permette di accedere all’oggetto creato, o, nel caso di __init__, che sta per essere creato.

 

 


Quando richiamiamo un metodo, non dobbiamo fare espressamente riferimento a self: ci pensa Python ad assegnarlo.

Il fatto che si chiami “self” è una pura convenzione, che, anche se siamo anticonformisti, ci conviene rispettare se teniamo alla leggibilità del nostro codice.


 

Vediamo ora all’opera il nostro nuovo costruttore:

 

creazione istanza della classe Cibo con costruttore

 

 

L’oggetto pasta ora ha tre nuovi attributi e possiamo provare a visualizzarne uno:

 

Visualizzare un attributo di un oggetto della classe

 

 

Sarebbe interessante aggiungere un po’ di ‘intelligenza’ alla nostra classe Cibo per calcolare, per esempio, le calorie di un alimento.

 

Con Python possiamo farlo interattivamente, creando una funzione e assegnandola come metodo della classe:

 

Funzione assegnata come metodo della classe

 

 


Le parentesi che “inglobano” l’espressione restituita dalla funzione, sono NECESSARIE solo se spezziamo l’istruzione su più righe.

Con le parentesi Python non segnalerebbe un errore di sintassi dovuto alla presenza del segno “+” a fine riga.

In alternativa si potrebbe inserire anche un backslash “\” come ultimo carattere della riga che prosegue a capo.


 

Abbiamo assegnato a posteriori alla nostra classe Cibo il metodo calcolaCalorie  e l’oggetto pasta che avevamo già creato lo ‘riceve’ automaticamente:

 

Metodo calcolaCalorie su oggetto pasta

 

 

Non è sorprendente il nostro Python?

 


Un metodo è una funzione abbinata all’oggetto, mentre un attributo è un dato che lo caratterizza.

Uno dei pilastri della programmazione object oriented sta proprio nel fatto che un oggetto “fonde” insieme sia i dati, sia la logica che opera su di essi.

Questo aspetto è denominato “incapsulamento”  in quanto “ingloba” in un’unica identità (ovvero nell’oggetto) due aspetti normalmente distinti:

i dati (sotto forma di attributi) e il codice che li gestisce (sotto forma di metodi).


 

Le classi e l’ereditarietà

 

Altro pilastro della programmazione object oriented è proprio l’ereditarietà, che possiamo utilizzare in modo estremamente semplice nelle nostre classi in Python.

Ecco la definizione:

 

“Una classe eredita  il comportamento di un’altra classe (la cosiddetta base class) estendendola però con funzionalità proprie. “

 

 

Come al solito, ricorriamo a un esempio per capire meglio tale concetto.

Estendiamo la nostra classe Cibo:

 

Classe estesa - Python

 

 

La nuova classe Verdura  è di tipo Cibo, ma con la particolarità che l’attributo grassi  è sempre uguale a 0.

 

Creiamo un’istanza di questa nuova classe e proviamo ad usare un metodo della classe base:

 

Utilizzo metodo della classe base nella classe estesa

 

 

Nella classe Verdura non c’è alcuna traccia del metodo calcolaCalorie.

Ma poiché tale metodo era definito nella classe base Cibo, lo possiamo usare tranquillamente anche nella classe derivata.

 

Nel costruttore abbiamo espressamente ripetuto le istruzioni di assegnamento degli attributi della classe base.

Potrebbe essere più comodo (e più semanticamente corretto ) richiamare il costruttore della classe base.

 

Vediamo quindi la classe Verdura modificata:

 

Classe estesa con richiamo al costruttore della classe base

 

 


Osserviamo la sintassi usata per richiamare il metodo costruttore originale: abbiamo usato il nome della classe base seguito da __init__ passandogli il parametro self.

Possiamo così, se ci serve, riutilizzare il costruttore della classe originaria.

In questo caso abbiamo semplicemente ‘forzato’ a 0 uno dei parametri (ovvero grassi ) che nella classe base poteva invece essere passato esplicitamente.


 

Le proprietà nelle classi in Python

 

Ripensiamo un attimo alla funzione calcolaCalorie definita all’inizio dell’articolo nella classe Cibo.

Non sarebbe più comodo poterla usare come se fosse un attributo, senza usare le parentesi?

Ebbene con Python possiamo farlo usando la funzione property:

 

Funzione property con le classi - Python

 

 

Adesso la nostra classe Cibo ha una proprietà calorie che, quando richiamata, provoca l’esecuzione della funzione che abbiamo indicato come primo parametro di property.

 

Le proprietà possono anche essere usate per definire funzioni che definiscono degli attributi oltre che restituirli.

Vediamo in che modo possiamo usarle nella nostra classe Verdura con l’attributo grassi:

 

esempio utilizzo delle proprietà per definire funzioni che definiscono attributi - Python

 

 

Vediamo cosa succede se definiamo una verdura con questa classe:

 

Utilizzo classe modificata - Python

 

 

In questo modo la nostra classe Verdura avrà sempre e comunque l’attributo grassi uguale a 0.

 

 


Esistono altri due parametri che possiamo usare con property:

– una funzione richiamata durante la cancellazione dell’attributo

– la documentazione per l’attributo (sì, esatto: sempre quella che viene visualizzata con help ).


 

Metodi privati 

 

Anche per le classi in Python esiste il concetto di metodo o attributo privato, presente anche in altri linguaggi di programmazione come C++ e Java.

 


Un metodo o un attributo privato non può essere richiamato dall’esterno della classe cui appartiene.


 

Ma a differenza di altri linguaggi di programmazione, per ottenere questa ‘privacy’ non abbiamo a disposizione una parola chiave, ma solo una convenzione di denominazione (in inglese naming convention ).

 

 


Un metodo o un attributo privato deve iniziare (ma non terminare) con una sequenza di due caratteri underscore “_”.


 

Proviamo ora a creare una classe con un metodo privato:

 

classe con metodo privato - Python

 

 

Creiamo un’istanza e proviamo a usare il metodo privato della classe:

 

esempio utilizzo metodo privato - Python

 

 

Come vediamo, Python non ha eseguito il metodo che avevamo definito, rispettando la convenzione che lo rende privato.

 

Proviamo ora ad eseguire dir sulla classe Archivio:

 

funzione dir su classe con metodo privato - Python

 

 

Il metodo __apri_file  ha subito un’operazione che in inglese viene definita name mangling: espressione traducibile come “storpiatura” dei nomi.

 

Se per qualsiasi motivo poi volessimo ad ogni costo e a nostro rischio e pericolo accedere a un metodo che avevamo definito privato, lo possiamo fare:

 

Accesso a metodo privato - Python

 

 


STAY-TUNED

 

 

Non è finita qui: per le classi in Python abbiamo ancora tante altre funzionalità, di cui parleremo nel prossimo articolo! Continua a seguirci e iscriviti alla nostra newsletter  per rimanere aggiornato!

 

Qui trovi tutto ciò che occorre sapere per utilizzare questo fantastico linguaggio di programmazione: Guida alla programmazione con Python.