Stampa  |  Nascondi immagini  |  Mostra immagini

Classi in python


Ecco di seguito la sintassi da utilizzare per definire una classe in python:

class <nome classe> [(<classe madre>,...)]:
<elenco dati membro da inizializzare>
<elenco metodi>

I dati membro si esprimono come le normali variabili di python. Se devo inizializzare dei dati membro faccio un semplice assegnamento, altrimenti posso definirli al momento dell'utilizzo, esattamente come avviene per le variabili normali.

Per i metodi basta utilizzare la medesima sintassi delle funzioni con alcuni accorgimenti.
Ogni metodo deve avere come primo parametro l'oggetto stesso, infatti ogni volta che viene richiamato il metodo, python sistema nel primo parametro il riferimento all'oggetto stesso. Questo permette di accedere ai dati membri appartenenti allo stesso oggetto, normalmente si usa chiamare questo parametro "self".
Vediamo l'esempio delle persone scritto praticamente in codice python:

class persona:
nome = ''
cognome = ''
indirizzo = ''
telefono = ''
stato_civile = ''

def cambia_indirizzo(self,s):
self.indirizzo = s
def cambia_telefono(self,s):
self.telefono = s
def cambia_stato_civile(self,s):
self.stato_civile = s
def stampa(self):
print self.nome,self.cognome,self.indirizzo,
self.stato_civile

class studente (persona): # ecco l'ereditarieta' !
scuola = ''
classe = 0

def cambia_scuola(self,s):
self.scuola = s
def promosso(self):
if self.classe == 5:
print 'scuola finita'
else:
self.classe = self.classe + 1

Se ora voglio istanziare un oggetto, è sufficiente richiamare il nome della classe come se fosse una funzione.
Se voglio accedere ai dati membri e ai metodi basta utilizzare il punto.

p1 = persona() # le parentesi sono obbligatorie
p1.nome = 'stefano' # assegnazione diretta ai dati membri
p1.cognome = 'riccio'
p1.cambia_indirizzo('via html n. 10') # richiamo un metodo
p1.stampa() # risultato : stefano riccio via html n. 10

p1 è un oggetto della classe persona che contiene quei valori.
Notate come abbiamo assegnato ai dati membri i valori direttamente oppure attraverso gli appositi metodi.
Ho utilizzato il metodo "cambia_indirizzo" passando un solo parametro (il metodo ne chiede due !), questo è stato possibile perché python associa al parametro "self" l'oggetto p1. Utilizzando self posso accedere direttamente ai dati membro all'interno della classe.
Se ora voglio istanziare un oggetto di tipo studente, posso utilizzare i stessi dati membri e metodi della classe progenitrice:

s1 = studente()
s1.nome = 'mario'
s1.cognome = 'rossi'
s1.cambia_indirizzo('via html n. 10')
s1.cambia_scuola('liceo')
s1.stampa() # risultato : mario rossi via html n. 10

Il risultato del metodo "stampa" non permette di visualizzare la scuola e la classe di appartenenza.
Questo avviene poiché viene richiamato il metodo della classe "persona" (classe madre), la quale non può conoscere il valore dei dati membri della classe "studente" (classe figlia).
Per risolvere questo problema ci viene in aiuto il polimorfismo, infatti è necessario ridefinire il metodo stampa costruendone una versione specifica per gli studenti.

Riscrivo la classe studente in questo modo:

class studente (persona):
scuola = ''
classe = 0

def cambia_scuola(self,s):
self.scuola = s
def promosso(self):
if self.classe == 5:
print 'scuola finita'
else:
self.classe = self.classe + 1
def stampa(self):
print self.nome,self.cognome,self.indirizzo,
self.stato_civile
print 'scuola: '+self.scuola+' classe '
+str(self.classe)

Il metodo stampa è polimorfico, sembra una brutta parola, ma questo significa che assume una forma diversa in base al tipo di oggetto sul quale viene applicato.

L'operazione di istanziazione di una classe provoca la creazione di un oggetto.
Spesso è necessario inizializzare i dati membri dell'oggetto nel momento della istanziazione di esso. Nel nostro esempio è utile inizializzare subito la persona con il suo nome e cognome (infatti il nome e cognome sono assegnati alla nascita e non cambiano più).
Per fare questo la teoria della programmazione orientata agli oggetti prevede l'uso di una funzione costruttore. In python esiste qualcosa di simile: è la funzione denominata "__init__()".

Quando all'interno di una classe viene dichiarata una funzione con questo nome, allora essa viene invocata automaticamente alla creazione dell'oggetto. Vediamo come modificare la classe "persona" per inizializzare automaticamente il nome e il cognome:

class persona:
indirizzo = ''
telefono = ''
stato_civile = ''

def __init__(self,n,c):
self.nome = n
self.cognome = c
def cambia_indirizzo(self,s):
self.indirizzo = s
def cambia_telefono(self,s):
self.telefono = s
def cambia_stato_civile(self,s):
self.stato_civile = s
def stampa(self):
print self.nome,self.cognome,self.indirizzo,
self.stato_civile

Con la chiamata:

p1 = persona('stefano','riccio')
p1.stampa() # risultato : stefano riccio

ottengo l'inizializzazione dei due dati membri. Da notare che ho eliminato l'assegnazione della stringa vuota alle due variabili (nome, cognome) nella classe. Esse infatti sono superflue poiché le variabili vengono create all'interno della funzione __init__.

In precedenza abbiamo parlato di incapsulamento e subito dopo non abbiamo applicato questo principio. Infatti la prima operazione fatta sull'oggetto p1 è stata quella di inizializzare i dati membri "nome e cognome" direttamente.
Python assume che tutti i dati membri siano pubblici e quindi modificabili dall'esterno dell'oggetto. Per indicare a python di tenere protetti i dati membri, e quindi realizzare veramente l'incapsulamento, si deve anteporre al nome della variabile i caratteri "__" ( 2 underscore).
Cosi facendo nessuno può modificare il dato senza utilizzare l'apposita funzione. Ad esempio, nella nostra classe "persona" è utile proteggere tutti i dati membri:

class persona:
__indirizzo = ''
__telefono = ''
__stato_civile = ''

def __init__(self,n,c):
self.__nome = n
self.__cognome = c
def cambia_indirizzo(self,s):
self.__indirizzo = s
def cambia_telefono(self,s):
self.__telefono = s
def cambia_stato_civile(self,s):
self.__stato_civile = s
def stampa(self):
print self.__nome,self.__cognome,self.__indirizzo,
self.__stato_civile

Se tento di assegnare direttamente "__nome" e "__cognome" il comando viene ignorato:

p1.__nome = 'prova'
p1.stampa()
# il risultato rimane: stefano riccio
via html n. 10

Esiste un altro concetto della programmazione orientata agli oggetti molto interessante. Esso consiste nella possibilità di effettuare il polimorfismo anche degli operatori, oltre che dei metodi.
Supponiamo di creare la classe dei numeri complessi nel seguente modo:

class num_comp:
parte_reale = 0
parte_immaginaria = 0

def somma(self,num):
# num è un oggetto di classe num_comp
self.parte_reale = self.parte_reale + num.parte_reale
self.parte_immaginaria = self.parte_immaginaria +
num.parte_immaginaria

def stampa(self):
print str(self.parte_reale) + '+'
+str(self.parte_immaginaria) + 'i'

Un numero complesso è composto da due valori: la parte reale e la parte immaginaria, e un metodo che ne fa la somma.
Per utilizzare questa classe devo scrivere il seguente programma:

n1 = num_comp()
n1.parte_reale = 1
n1.parte_immaginaria = 1

n2 = num_comp()
n2.parte_reale = 2
n2.parte_immaginaria = 3

n1.somma(n2)
n1.stampa() # risultato 3+4i

La sintassi, purtroppo, risulta piuttosto articolata. Che bello sarebbe poter scrivere (n1 + n2) !, proprio come si fa in matematica !.
Per poter scrivere questo è necessario rendere l'operatore "+" del python polimorfico, in modo che si accorga di lavorare con i numeri complessi e richiami la giusta funzione.

Per fare questo si deve dichiarare la classe nel seguente modo:

class num_comp:
parte_reale = 0
parte_immaginaria = 0

def __add__(self,num):
ris = num_comp()
ris.parte_reale = self.parte_reale + num.parte_reale
ris.parte_immaginaria = self.parte_immaginaria
+ num.parte_immaginaria
return ris

def stampa(self):
print str(self.parte_reale) + '+'
+str(self.parte_immaginaria) + 'i'

Ho dichiarato una funzione speciale, denominata "__add__", in grado di effettuare l'overloading (la sostituzione) dell'operatore ADD (+).
Essa crea un nuovo oggetto e somma le singole variabili. Vediamo la nuova sintassi di chiamata della funzione:

n1 = num_comp()
n1.parte_reale = 1
n1.parte_immaginaria = 1

n2 = num_comp()
n2.parte_reale = 2
n2.parte_immaginaria = 3

r = n1 + n2 # come in matematica !
r.stampa() # risultato 3+4i

Attraverso l'overloading degli operatori si possono creare classi molto sofisticate utilizzabili con una sintassi veramente molto elegante.

Di seguito vi presento una tabella con alcune funzioni speciali con le quali è possibile effettuare l'overloading:

Metodo specialeDescrizioneEsempi
__init__Costruttore di oggettop1 = persona()
__add__Operatore di somman1 + n2
__or__Operatore ORx ¦ y
__len__Lunghezzalen(x)
__cmp__Confrontox == y

I sorgenti python sono raggruppati in moduli. Anche i moduli, come le classi, sono uno strumento per organizzare bene un programma e ottenere una componentistica da riutilizzare in futuro.
Si pone il problema di come organizzare le classi nei moduli. è possibile inserire più classi nello stesso modulo, ma io consiglio di utilizzare un modulo per ogni classe.
In questo modo i files sul disco equivalgono alle classi implementate e si ottiene un miglior ordine del programma. Si troveranno bene con questo sistema coloro che provengono dal linguaggio Delphi !

Una considerazione finale prima di concludere l'argomento della programmazione ad oggetti.
è difficile esaurire l'argomento della programmazione ad oggetti, poiché esso è molto vasto e articolato. Bisogna tenere in considerazione che ancora oggi è difficile programmare bene ad oggetti per diversi motivi:

  • Ogni linguaggio di programmazione implementa in modo diverso e solo in parte le linee teoriche della programmazione ad oggetti (anche python non fa eccezione).
  • La maggior parte dei linguaggi sono ibridi e quindi non obbligano il programmatore ad usare correttamente gli oggetti.
  • Non è semplice modellare un problema della vita reale ad oggetti per trasformalo in un programma.

Python è un linguaggio ibrido, e quindi lascia libero il programmatore di scegliere se utilizzare o meno gli oggetti. Questo potrebbe essere un punto a sfavore di python rispetto ad altri linguaggio object orientd puri, come java per esempio.
Il mio consiglio è quello di iniziare con qualche piccolo esempio orientato ad oggetti e cercare di lavorare in quella direzione, risulta sicuramente un software più pulito ed elegante.



Versione originale: http://programmazione.html.it/guide/lezione/1301/classi-in-python/

© 1997-2006 HTML.it
La vendita, il noleggio, il prestito e la diffusione del contenuto di questa pagina sono vietate, tranne nei casi specificati nella pagina http://www.html.it/info/note-legali.php