vuoi
o PayPal
tutte le volte che vuoi
PARAMETRI OPZIONALI
Nei parametri formali di una funzione possono essere inseriti anche dei parametri opzionali:
Esempio: Utilizzo: Viene utilizzato il
def interessi(capitale, tasso=0.05): >>>s=interessi(100) valore di default
i=capitale*tasso >>>print(s) se non è specificato
return i 5 il parametro opzionale
c = interessi(prestito) >>>print(interessi(100, tasso=0.12))
… 12
Valore di default Nell’invocazione di una
del parametro funzione, i parametri
opzionali vanno sempre
opzionale per ultimi
Data una funzione: def fun1(par1, par2, opz1=100, opz2=False):
……
I parametri attuali vanno disposti in questo modo: >>>a=fun1(10, 20, opz2=True, opz1=2)
i parametri obbligatori vengono mappati sulla base della posizione
Namespace:
i parametri opzionali vengono mappati in base al nome citato
par1=10
par2=20
opz1=2
opz2=True
METODI PARAMETRI OPZIONALI
I parametri opzionali possono essere usati sia nelle funzioni sia nei metodi degli oggetti.
Es. def open(filename, mode, encoding=‘ascii’, errors=‘ignore’)
#codice
Es. uso dei parametri opzionali nei metodi : Class CsvReader4:
def __init__(self, fname):
self.f=open(fname,'r’)
def readRecord(self, sep=‘;’):
line = self.f.readline()
if line=='':
return []
else:
line = line.strip('\n')
records = line.split(sep)
return records
cr = CsvReader4('prova.csv')
r=cr.readRecord()
while r!=[]:
print(r)
r=cr.readRecord()
EREDITARIETA’
L’ereditarietà rappresenta una relazione tra :
– Una superclasse (classe padre), più generica
– Una sottoclasse (classe figlia), più specifica
La classe figlia eredita i dati (attributi) e il comportamento
(metodi) dalla classe padre.
Vantaggi:
• Il vantaggio dell’ereditarietà è quello di condividere parti di codice
• Permette di rappresentare oggetti aventi comportamenti diversi
Spesso la diversità tra oggetti può essere espressa con una semplice variabile di oggetto, in questo
caso non serve l’ereditarietà.
Esempio:
– Se due veicoli differiscono solo per il consumo di benzina, usa una variabile di oggetto (es. kmPerLitro)
– Se due veicoli si comportano in modo differente, usa l’ereditarietà (es. auto e camion)
SOTTOCLASSE
In Python si definisce una sottoclasse specificando cosa la rende diversa dalla sua superclasse.
Le sottoclassi ereditano dalla superclasse: – tutte le variabili di oggetto
– tutti i metodi che non si modificano
La sottoclasse può aggiungere: – Variabili nuove (non presenti nella superclasse)
– Metodi nuovi (non presenti nella superclasse)
– Metodi che modificano il comportamento di metodi ereditati
(sovrascrivono = override)
Esempio:
class Animale: #classe padre
def __init__(self, nome): • def class Figlia(Padre):
self.nome = nome La classe Figlia eredita dalla classe Padre tutto.
def parla(self): es.: – il costruttore,
print(“”) – l’attributo self.nome
class Gatto(Animale): #classe figlia
def parla(self): #overriding
print("Miao!”) • Overriding: quando una classe Figlia ridefinisce
class Cane(Animale): #classe figlia un metodo della classe Padre
def parla(self): #overriding Es., il metodo parla()
print("Bau!”)
g = Gatto("Garfield") • parla() e' un esempio di polimorfismo. Ha un
g.parla() #Miao! comportamento diverso a seconda dell’oggetto
c = Cane("Rocky") su cui viene invocato.
c.parla() #Bau!
COSTRUTTORE DELLA SOTTOCLASSE In Python 2:
nomeSuperclasse.__init__(self,
parametri)
In Python 3:
super() .__init__(parametri)
METODI DELLA SOTTOCLASSE : OVERRIDING e OVERLOADING
Metodi di una sottoclasse ereditati dalla superclasse si comportano nello stesso modo
Se volete modificare il comportamento di un metodo
– Scrivete un metodo nella sottoclasse con stesso nome, stessi parametri, ma nuova implementazione
– Questo metodo sovrascrive il metodo della superclasse
Il nuovo metodo sarà invocato con lo stesso nome quando è chiamato su un oggetto della sottoclasse
OVERLOADING = creazione di un metodo con stesso nome e parametri diversi
I linguaggi ad oggetti (es. Java, C++) permettono di creare in una classe diverse versioni dello stesso metodo
(stesso nome, diversi parametri).
In python quando si crea un metodo con lo stesso nome di un metodo precedente, il metodo precedente
viene sovrascritto in Python questo metodo non è ammesso.
Esempio: class CsvReader3:
class CsvReaderSbagliato: ma può essere simulato
def __init__(self, fname):
def __init__(self, fname): utilizzando parametri opzionali self.f=open(fname,'r')
self.f=open(fname,'r') def readRecord(self, sep=‘;’):
def readRecord(self): line = self.f.readline()
line = self.f.readline() if line=='':
if line==””: return []
return [] else:
else: line = line.strip('\n')
line = line.strip('\n') return(line.split(';'))
return(line.split(';'))
def readRecord(self, sep): Questo metodo sovrascrive cr1 = CsvReader3('prova.csv')
line = self.f.readline() il precedente r1=cr1.readRecord() #ok
if line=='’: cr2 = CsvReader3(’dat.csv')
return [] Viene aggiunto automaticamente r2=cr2.readRecord(sep=‘,’) #ok
else: r come primo parametro del
line = line.strip('\n') metodo
return(line.split(sep)) Uso il parametro opzionale
per specificare un differente
cr = CsvReaderSbagliato('prova.txt') separatore di informazioni
r=cr.readRecord() #cerca di richiama il 1° metodo
#TypeError: readRecord() takes exactly 2 arguments (1 given)
EREDITARIETA’ MULTIPLA
class Camion(): Una classe può ereditare da più padri
def __init__(self, targa, proprietario): class Figlia(Padre1, Padre2, …):
self.targa=targa La figlia eredita tutti i metodi ed attributi delle classi padri.
self.prop=proprietario
def getNumRuote(self): La classe figlia può effettuare l’overriding di metodi,
return 6 es. getNumRuote().
def getProprietario(self):
return self.prop
class Cisterna():
def setQtTrasp(self, qt):
self.qt=qt
def getQtTrasp(self):
return self.qt
class Autocisterna(Camion, Cisterna):
def getNumRuote(self):
return 8
a=Autocisterna('DF645HK','Rossi')
a.setQtTrasp(50)
print(a.getQtTrasp()) #50
print(a.getNumRuote()) #8
print(a.getProprietario()) #Rossi
CONFLITTI
class Auto(): • Conflitto: due classi padri offrono un metodo con lo stesso nome.
def getNumRuote(self): Ma in python, può esistere un solo metodo con lo stesso nome.
return 4
def getNumPosti(self): • Criteri di scelta del metodo che la figlia erediterà (da alto al basso):
return 5 – metodo nella classe figlia che fa overriding (se presente)
– la classe padre che appare più a sinistra tra le ()
class Barca(): nella dichiarazione della classe figlia
def getLunghezza(self):
return 5 Es. getNumPosti().
def getNumPosti(self): – No overriding: in Anfibio, non è definito getNumPosti()
return 10 – getNumPosti() di Barca prevale sul metodo della classe Auto
class Anfibio(Barca, Auto):
def colore(self):
return "Rosso"
af = Anfibio()
print(af.getNumRuote()) #4
print(af.getLunghezza()) #5
print(af.getNumPosti()) #10
INCAPSULAMENTO
Interfaccia pubblica della classe = insieme di tutti i metodi della classe, e la descrizione del comportamento
Incapsulamento = è la strategia di mettere a disposizione un’interfaccia pubblica, mantenendo nascosti i
dettagli implementativi.
Es. usando un programma che è operativo da molto tempo, è normale che alcuni dettagli implementativi
siano cambiati per rendere gli oggetti più efficienti, tenendo nascosti tali dettagli i miglioramenti non
costringono chi usa il programma a fare modifiche dei dettagli implementativi.
import codecs #Gest. file tradizionale
f = codecs.open(’file.txt', ‘r’, encoding='utf-16’) f = open(’file.txt', ‘r’)
s=f.readline() s=f.readline()
f.seek(15) f.seek(15)
f.close() f.close()
Incapsulamento e Polimorfismo
– Entrambi gli oggetti condividono un’interfaccia comune (metodi readline, seek, close …)
– Diversa implementazione
– Comportamento diverso a seconda del contesto
(scrivere/posizionarsi su un file UTF16 è diverso dallo scrivere/posizionarsi su un file ASCII)
ENCAPSULATION: PROBLEMA
Esempio: Il programma utilizzatore della class Studente ha avuto accesso alla struttura dati interna della classe.
La struttura dati interna della classe è cambiata MA non è stato adeguato il codice del programma utilizzatore.
class Studente():
def __init__(self, nome):
self.nome = nome con s.nome accedo
direttamente all'attributo
s=Studente("Mario Rossi")
print(s.nome.upper()) #MARIO ROSSI s.nome è una stringa
#Modifico la classe Studente su cui posso richiamare il
class Studente(): metodo upper()
def __init__(self, nome):
self.nome = nome.split(' ')
s=Studente("Mario Rossi")
print(s.nome.upper()) AMO FOLLEMENTE GIOGOTTA E LO SPOSERO’
#AttributeError: 'list' object has no attribute 'upper' class Studente2():
RISOLVO IL PROBLEMA class Studente2(): def __init__(self, nome):
def __init__(self, nome): self._nome = nome.split(’ ‘)
self._nome = nome def getNome(self):
def getNome(self): st=""
return self._nome for el in self._nome:
st=st+el+' '
Le modifiche al codice sono frequenti, per modificare return st
(tutti) i programmi che utilizzano una classe
modificando solo la classe uso l’encapsulation.
s=Studente2("Mario Rossi")
print(s.getNome().upper()) #MARIO ROSSI
ENCAPSULATION
Si creano dei metodi per accedere ai dati: cioè un’interfaccia, progettata per rimanere inalterata nel tempo.
class Studente2():
def __init__(self, nome):
…
def getNome(self):
…
Nascondi le strutture dati e le implementazioni sottostanti. L’obiettivo è quello di forzare l’accesso ai dati
attraverso i metodi dell’interfaccia.
Per nascondere gli attributi INFORMATION HIDING
Convenzione: non si accede dall’esterno di una classe agli attributi o metodi il cui nome inizia con _
E’ usato per indicare attributi che saranno modificati a breve; essendo una convenzione può essere disattes