EREDITARIETA’
La prima cosa da dire parlando di ereditarietà in Python è che il linguaggio consente ad una classe di ereditare gli attributi e i metodi di un’altra classe. Le sottoclassi o classi derivate che sono le più specializzate ereditano gli attributi e metodi dalle loro classi base o superclassi. Le classi derivate possono ridefinire il comportamento degli attributi ereditati (metodi) mediante override e possono aggiungere delle funzionalità aggiuntive che tendono a specializzare la classe. Le superclassi in genere sono più generiche rispetto alle loro sottoclassi. Quando andiamo a creare un’istanza di una sottoclasse questa risulta essere anche istanza di tutte le superclassi della classe di cui è istanza.
LA FUNZIONE ISINSTANCE
Esiste una funzione isinstance che prende come primo parametro l’istanza e come secondo parametro la classe che si vuol verificare. La funzione restituisce True se ne è istanza False in caso contrario. m1 essendo istanza di AClass è implicitamente istanza anche di BClass, inoltre m1 eredita da BClass sia il costruttore __init_ sia il metodo di istanza printMessage(self) come si può vedere dall’esempio.
OVERRIDE
Con l’override possiamo ridefinire gli attributi (metodi) nelle sottoclassi. Nella classe derivata AClass stiamo ridefinendo il comportamento di printMessage(self) anche se in maniera minimale. Quando creiamo un’istanza di AClass che chiamiamo m1 e richiamiamo printMessage(self) di fatto stiamo richiamando il metodo della classe derivata in quanto abbiamo sovrascritto quello della classe base. Il codice che abbiamo scritto tuttavia presenta un problema. Se creiamo un costruttore anche nella classe derivata alla creazione dell’istanza verrà invocato il costruttore della sottoclasse, quello della superclasse verrà ignorato non impostando correttamente l’attributo message. Questo perché viene eseguito l’override del costruttore della superclasse. Per risolvere questo problema si usa la funzione super().
LA FUNZIONE SUPER()
super() ci consente di accedere ai metodi delle superclassi dalle sottoclassi. La funzione super() fa in modo che venga eseguito il costruttore della superclasse, così passando opportunamente l’attributo message, che riceviamo nel costruttore della sottoclasse, tutti gli attributi verranno inizializzati correttamente.
PROPERTIES
Normalmente nei linguaggi Object Oriented si tende a incapsulare o rendere privati i dati con dei metodi getter e setter e rendere pubblici i metodi ove necessario. Python non supporta pienamente l’incapsulamento. Si possono creare tramite decoratori getter e setter di una proprietà.
class persona(object): def __init__(self,nome,cognome,indirizzo): self.__nome_ = nome self.__cognome_ = cognome self.__indirizzo_ = indirizzo @property def nome(self): return self.__nome_ @nome.setter def nome(self,nome): self.__nome_ = nome @property def cognome(self): return self.__cognome_ @cognome.setter def cognome(self,cognome): self.__cognome_ = cognome @property def indirizzo(self): return self.__indirizzo_ @indirizzo.setter def indirizzo(self,indirizzo): self.__indirizzo_ = indirizzo def denominazione(self): s= f'Persona Nome: {self.nome} Cognome: {self.cognome} indirizzo: {self.indirizzo}' print(s) class utente(persona): def __init__(self,id,numeroconto,saldo,nome,cognome,indirizzo): self.__id_ = id self.__numeroconto_ = numeroconto self.__saldo_ = saldo super().__init__(nome,cognome,indirizzo) @property def id(self): return self.__id_ @id.setter def id(self,id): self.__id_ = id @property def numeroconto(self): return self.__numeroconto_ @numeroconto.setter def numeroconto(self,numeroconto): self.__numeroconto_ = numeroconto @property def saldo(self): return self.__saldo_ @saldo.setter def saldo(self,saldo): if (saldo < 0): s=f'Impossibile impostare il saldo, valore passato: {saldo}' print(s) return else: self.__saldo_ = saldo def saldoCorrente(self): s = f'Utente: nome {self.nome} cognome {self.cognome} saldo {self.saldo}' print(s) class banca(): def __init__(self,id,denominazione,abi,cab,utente): self.__id_=id self.__denominazione_=denominazione self.__abi_=abi self.__cab_ = cab self.__utente_=utente @property def id(self): return self.__id_ @id.setter def id(self,id): self.__id_ = id @property def denominazione(self): return self.__denominazione_ @denominazione.setter def denominazione(self,denominazione): self.__denominazione_ = denominazione @property def abi(self): return self.__abi_ @abi.setter def abi(self,abi): self.__abi_ = abi @property def cab(self): return self.__cab_ @cab.setter def cab(self,cab): self.__cab_ = cab @property def utente(self): return self.__utente_ @utente.setter def utente(self,utente): self.__utente_ = utente def preleva(self,importo,utente): if importo < 0 or importo > utente.saldo: s = f'Impossibile prelevare, importo passato: {importo} saldo: {utente.saldo}' print(s) return else: utente.saldo -= importo def accredita(self,importo,utente): if importo < 0: s = f'Impossibile accreditare, valore passato: {importo}' print(s) return else: utente.saldo += importo u1 = utente(12345,"AS67890OL",12000.0,'Mario','Rossi','via delle rose 54') u2 = utente(678345,"AS67L768",2000.0,'Luigi','Verdi','via della fontana 7') u1.denominazione() u2.denominazione() u1.saldoCorrente() u2.saldoCorrente() b1=banca(9873456,'UBI BANCA', 30600,10400,u1) b2=banca(9876832,'UBI BANCA', 30600,10400,u2) b1.preleva(5000.0,u1) u1.saldoCorrente() b2.accredita(3000.0,u2) u2.saldoCorrente()
APPROFONDIMENTO
L’ereditarietà è un concetto fondamentale nella programmazione orientata agli oggetti (OOP) in Python. Consente di creare una nuova classe (classe derivata) che eredita attributi e metodi da un’altra classe (classe base o genitore). Questo permette di riutilizzare il codice e di estendere le funzionalità delle classi esistenti.
Concetti chiave
•Classe Base (Genitore): La classe da cui vengono ereditati gli attributi e i metodi.
•Classe Derivata (Figlio): La classe che eredita dalla classe base.
Sintassi base
Ecco un esempio di come funziona l’ereditarietà in Python:
# Definizione della classe base
class Animale:
def __init__(self, nome):
self.nome = nome
def parla(self):
return f”{self.nome} fa un suono.”
# Definizione della classe derivata
class Cane(Animale):
def __init__(self, nome, razza):
super().__init__(nome)
self.razza = razza
def parla(self):
return f”{self.nome} abbaia.”
# Utilizzo delle classi
mio_cane = Cane(“Fido“, “Labrador“)
print(mio_cane.parla()) # Output: Fido abbaia.
Spiegazione
1. Classe Base (Animale): Definiamo una classe chiamata Animale con un attributo nome e un metodo parla che restituisce una stringa generica.
2. Classe Derivata (Cane): La classe Cane eredita dalla classe Animale. La funzione super() viene utilizzata per richiamare il costruttore della classe base (__init__), permettendo alla classe derivata di inizializzare gli attributi della classe base.
3. Override dei Metodi: Nella classe Cane, il metodo parla viene sovrascritto (override) per fornire un comportamento specifico per i cani.
Ereditarietà multipla
Python supporta anche l’ereditarietà multipla, dove una classe derivata può ereditare da più classi base:
class A:
def metodo_a(self):
return “Metodo A“
class B:
def metodo_b(self):
return “Metodo B“
class C(A, B):
pass
oggetto = C()
print(oggetto.metodo_a()) # Output: Metodo A
print(oggetto.metodo_b()) # Output: Metodo B
In questo esempio, la classe C eredita da entrambe le classi A e B, e quindi ha accesso ai metodi di entrambe. L’ereditarietà è un potente strumento per modellare relazioni tra oggetti e per promuovere il riutilizzo del codice.
Lascia un commento