Introduzione all' OO in Python Linguaggi di Programmazione: Paradigmi di Programmazione (Sperimentazioni) Matteo Baldoni Dipartimento di Informatica Universita` degli Studi di Torino C.so Svizzera, 85 I-049 Torino baldoni@di.unito.it http://www.di.unito.it/~baldoni/didattica Funzioni in Python def MCD(a, b): Calcola il MCD while not z == w: if z < w: temp = z z = w w = temp z = z - w return z >>> print MCD(2,8) 4 Python accetta definizioni di funzioni senza che queste siano necessariamente metodi di oggetti Una funzione e` definita mediante la parola chiave def Una stringa delimitata da doppi apici permette di documentare la funzione stessa Il corpo della funzione e` indentato rispetto alla def
Funzioni in Python >>> print type(mcd) <type 'function'> >>> print MCD <function MCD at 0x80d7d3c> >>> mcd = MCD >>> print mcd(2,8) 4 La definizione di una funzione corrisponde alla creazione di un oggetto di tipo function il cui riferimento e` contenuto nel nome della funzione stessa >>> print type(mcd), mcd <type 'function'> <function MCD at 0x80d7d3c> Un esempio: il quicksort def quicksort(list): def partition(pivot,list): slist = [] # nota slist = glist = [] e` errato, glist = [] # punterebbero alla stessa struttura lista! for x in list: if x > pivot: # se cpm e` definito glist += [x] slist += [x] return slist, glist if len(list) <= : return list slist, glist = partition(list[0],list[:]) return quicksort(slist) + [list[0]] + quicksort(glist) E` possibile definire funzioni annidate In realta` la partition restituisce una tupla e non due valori distinti >>> print quicksort([4,2,6,,8,5,3,9,8,3,5,4]) 2
Definizione di una classe Questa classe definisce un conto corrente arrayconti = [] numeroconti = 0 Una classe e` definita mediante la parola chiave class seguita dal nome della classe e : def versamento(self, valoreversamento): if not checkinteger(valoreversamento): raise OperazioneErrata("Valore non intero") if valoreversamento < 0: raise OperazioneErrata("Valore versamento negativo") self.aggiornainteressi() self.saldo += valoreversamento ContoCorrente.totaleSoldiBanca += valoreversamento Stringa di documentazione della classe E` importante l'indentazione nella definizione dell'intera classe Definizione di metodi arrayconti = [] numeroconti = 0 Un metodo e` una funzione definita all'interno di una definizione di classe def versamento(self, valoreversamento): if not checkinteger(valoreversamento): raise OperazioneErrata("Valore non intero") if valoreversamento < 0: raise OperazioneErrata("Valore versamento negativo") self.aggiornainteressi() self.saldo += valoreversamento ContoCorrente.totaleSoldiBanca += valoreversamento Non e` necessario che questo parametro abbia nome self ma e` solo per tradizione Il primo parametro contiene sempre il riferimento esplicito (rispetto a Java) all'oggetto su cui e` invocato il metodo stesso 3
Costruttori arrayconti = [] numeroconti = 0 Parametro inizializzato a zero se non specificato Il costruttore e` identificato da init In Python non e` permesso l'overloading ma e` possibile avere parametri inizializzati di default def init (self, nomec, saldo = 0): self.nomecorrentista = nomec self.numeroconto = ContoCorrente.numeroConti ContoCorrente.numeroConti += self.saldo = saldo ContoCorrente.totaleSoldiBanca += saldo self.dataultimaoperazione = Data(ContoCorrente.data.getGiorni()) self.interessematurati = 0 (ContoCorrente.arrayConti).append(self) Altri metodi speciali sono str, cpm, del Variabili di istanza e di classe arrayconti = [] numeroconti = 0 Variabili statiche Le variabili statiche sono quelle inizializzate allo stesso dei metodi Le variabili inizializzate come attributi di self sono variabili di istanza def init (self, nomec, saldo = 0): self.nomecorrentista = nomec self.numeroconto = ContoCorrente.numeroConti ContoCorrente.numeroConti += self.saldo = saldo ContoCorrente.totaleSoldiBanca += saldo self.dataultimaoperazione = Data(ContoCorrente.data.getGiorni()) self.interessematurati = 0 (ContoCorrente.arrayConti).append(self) Variabili di istanza: gli attributi di istanza sono creati quando inizializzati la prima volta (in un qualsiasi metodo) Attualmente non e` possibile definire metodi di classe (statici) 4
Creare oggetti ed invocare metodi cc0 = ContoCorrente("Matteo", 20) cc = ContoCorrente("Mattia") cc2 = ContoCorrenteConSpese("Baldoni") ContoCorrente.data.incrementaSettimana() cc.versamento(20) ContoCorrente.data.incrementaSettimana() cc2.versamento(20) print cc print cc2 >>> type(cc) <type 'instance'> >>> type(contocorrente) <type 'class'> Tipi primitivi presenti in Python La creazione di oggetti e` effettuata invocando il nome della classe con gli opportuni parametri Non e` necessaria la parola chiave new come in Java I metodi si invocano con la classica notazione oggetto.metodo(lista di parametri) Le classi in Python non definiscono un tipo! (lo saranno in Python 3) Creazioni di sottoclassi class ContoCorrenteConSpese(ContoCorrente): costooperazione =.50 def init (self, nomec, saldo = 0): ContoCorrente. init (self, nomec, saldo) self.numerooperazioni = 0 def versamento(self, valoreversamento): ContoCorrente.versamento(self, valoreversamento) self.numerooperazioni += Non esiste il costrutto super E` possibile l'overrinding di un qualsiasi attributo di un oggetto: costruttore, i metodi, i metodi speciali e variabili Le sopraclassi (e` permessa l'ereditarieta` multipla) sono indicate in una lista tra parentesi tonde dopo il nome della classe E` possibile richiamare i metodi delle sopraclassi facendo riferimento direttamente ad essi come attributi di classe. L'ereditarieta` multipla e` risolta con una ricerca depth-first 5
Binding dinamico in Python class ProvaUno: def init (self, x=0): print self.x def stampax(self): print "Valore di x: ", self.x def stampacampi(self): self.stampa() class ProvaDue(ProvaUno): def init (self, x=20, y=30): self.y = y print self.x, self.y if name == ' main ': pu = ProvaUno() pd = ProvaDue() pu.stampacampi() pd.stampacampi() pu.stampax() pd.stampax() Anche in Python in binding e` dinamico ma a differenza di Java questo vale per ogni attributo di un oggetto, variabili di classe incluse Identificatori privati e binding dinamico class ProvaUno: def init (self, x=0): self. y = x print self.x, self. y def stampax(self): print "Valore di x: ", self.x, self. y def stampacampi(self): self.stampa() class ProvaDue(ProvaUno): def init (self, x=20, y=30): ProvaUno. init (self) self. y = x self.y = y print self.x, self.y, self. y if name == ' main ': pu = ProvaUno() pd = ProvaDue() pu.stampacampi() pd.stampacampi() pu.stampax() pd.stampax() Ogni attributo di un oggetto e` implicitamente dichiarato pubblico E` possibile definire un attributo privato anteponendo ad un identificatore Un identificatore prefisso da viene sostituito prefisso da _NomeDellaClasse Riguardo il binding dinamico il comportamento e` quindi simile a quello dei metodi privati di Java! 6
Identificatori privati e binding dinamico class ProvaUno: def init (self, x=0): self._provauno y = x print self.x, self._provauno y def stampax(self): print "Valore di x: ", self.x, self._provauno y def stampacampi(self): self.stampa() class ProvaDue(ProvaUno): def init (self, x=20, y=30): ProvaUno. init (self) self._provadue y = x self.y = y print self.x, self.y, self._provadue y if name == ' main ': pu = ProvaUno() pd = ProvaDue() pu.stampacampi() pd.stampacampi() pu.stampax() pd.stampax() E` chiaro ora che non si applica il binding dinamico semplicemente perche` non si tratta di overriding di identificatori Eccezioni in Python def versamento(self, valoreversamento): if not checkinteger(valoreversamento): raise OperazioneErrata("Valore non intero") if valoreversamento < 0: Il trattamento delle eccezioni in Python e` simile a quella in Java raise OperazioneErrata("Valore versamento negativo") self.aggiornainteressi() self.saldo += valoreversamento ContoCorrente.totaleSoldiBanca += valoreversamento try: cc = ContoCorrente("Matteo") cc.versamento(20) ContoCorrente.data.incrementaSettimana() ContoCorrente.data.incrementaSettimana() cc.versamento(0). except OperazioneErrata, args: print "Operazione SBAGLIATA:", args Le eccezioni sono eggetti definibili dall'utente 7
Import di definizioni (in particolare classi) import Data from Comparabile import Comparabile from OperazioneErrata import * Si accede con la notazione Data.nome importa ogni definizione (funzioni, classi, variabili) contenuta nel file OperazioneErrata.py Ogni file definisce implicitamente un modulo il cui nome corrisponde al nome assegnato al file E` possibile importare le definizioni di un modulo utilizzandi l'istruzione import Se vi sono piu` import dello stesso modulo questo viene comunque caricato solo una volta Una speficica definizione puo` essere importata mediante il costrutto from import 8