Programmazione Orientata agli Oggetti in Python: Ereditarietà

Slide dall'Università sulla Programmazione Orientata agli Oggetti in Python: Ereditarietà. Il Pdf esplora l'ereditarietà singola e multipla in Python, con esempi di codice commentato, ed è utile per lo studio autonomo di Informatica a livello universitario.

Mostra di più

14 pagine

Programmazione Orientata agli Oggetti in Python:
Ereditarietà
Esempio di ereditarietà con Python
Immaginiamo di aver definito una classe Parallelogrammi, che descrive tutti i quadrilateri che hanno i lati opposti uguali a
due a due.
Questa classe ha come attributi le lunghezze di due lati adiacenti, lato1 e lato2, e come metodo il calcolo della lunghezza del
perimetro, che per qualsiasi parallelogramma vale 2*(lato1+lato2).
I rettangoli sono un caso particolare all¾interno della classe Parallelogrammi: sono infatti i parallelogrammi che hanno tutti gli
angoli interni uguali a 90°.
Se si vuole definire una classe Rettangoli, perc, non occorre ripartire da zero.
Conviene invece crearla a partire dalla classe Parallelogrammi, facendole ereditare le proprietà di quella classe.
Si dice allora che Parallelogrammi è la classe base, o superclasse, mentre Rettangoli è una classe derivata, o sottoclasse.
Grazie all¾ereditarietà, la classe derivata potrà riutilizzare i membri della classe base.
Codice Python completo
class Parallelogrammi: # classe base
def __init__(self, l1, l2, ar):
self.lato1 = l1
self.lato2 = l2
self.angoli_retti = ar
def perimetro(self):
return (self.lato1 + self.lato2) * 2
def descrizione(self):
if self.angoli_retti == False:
print('il quadrilatero è un parallelogramma')
else:
print('il quadrilatero è un rettangolo')
class Rettangoli(Parallelogrammi): # sottoclasse
def __init__(self, l1, l2, ar): # membri ereditati
Parallelogrammi.__init__(self, l1, l2, ar)
def area(self): # metodo specifico
return self.lato1 * self.lato2
print('creo un oggetto della sottoclasse:')
ret = Rettangoli(7.4, 10, True)
ret.descrizione()
print('i lati misurano', ret.lato1, 'e', ret.lato2)
print('il perimetro misura', ret.perimetro())
print("l'area misura", ret.area())
Spiegazione del codice
Parallelogrammi è la superclasse con attributi lato1, lato2, angoli_retti e due metodi: perimetro() e descrizione().
Rettangoli eredita tutto da Parallelogrammi e aggiunge un metodo proprio: area().
Quando creiamo un oggetto ret della sottoclasse Rettangoli, possiamo usare tutti i metodi della superclasse e anche quelli
nuovi.

Visualizza gratis il Pdf completo

Registrati per accedere all’intero documento e trasformarlo con l’AI.

Anteprima

Programmazione Orientata agli Oggetti in Python: Ereditarietà

parent + vd child : ())) : your (insertance)

Programmazione Orientata agli Oggetti in Python: Ereditarietà child classes child ab astt) : (crinast (classes)

Esempio di ereditarietà con Python

@ BarchPythomEsempio di ereditarietà con Python Immaginiamo di aver definito una classe Parallelogrammi, che descrive tutti i quadrilateri che hanno i lati opposti uguali a due a due. Questa classe ha come attributi le lunghezze di due lati adiacenti, lato1 e lato2, e come metodo il calcolo della lunghezza del perimetro, che per qualsiasi parallelogramma vale 2*(lato1+lato2). I rettangoli sono un caso particolare all'interno della classe Parallelogrammi: sono infatti i parallelogrammi che hanno tutti gli angoli interni uguali a 90°. Se si vuole definire una classe Rettangoli, perciò, non occorre ripartire da zero. Conviene invece crearla a partire dalla classe Parallelogrammi, facendole ereditare le proprietà di quella classe. Si dice allora che Parallelogrammi è la classe base, o superclasse, mentre Rettangoli è una classe derivata, o sottoclasse. Grazie all'ereditarietà, la classe derivata potrà riutilizzare i membri della classe base.

Codice Python completo

class Parallelogrammi: # classe base def_init_(self, 11, 12, ar): self.lato1 = l1 self.lato2 = l2 self.angoli_retti = ar def perimetro(self): return (self.lato1 + self.lato2) * 2 def descrizione(self): if self.angoli_retti == False: print('il quadrilatero è un parallelogramma') else: print('il quadrilatero è un rettangolo') class Rettangoli(Parallelogrammi): # sottoclasse def_init_(self, 11, 12, ar): # membri ereditati Parallelogrammi ._ init_(self, 11, 12, ar) def area(self): # metodo specifico return self.lato1 * self.lato2 print('creo un oggetto della sottoclasse:') ret = Rettangoli(7.4, 10, True) ret.descrizione() print('i lati misurano', ret.lato1, 'e', ret.lato2) print('il perimetro misura', ret.perimetro()) print("l'area misura", ret.area())

Spiegazione del codice

  • Parallelogrammi è la superclasse con attributi lato1, lato2, angoli_retti e due metodi: perimetro() e descrizione().
  • Rettangoli eredita tutto da Parallelogrammi e aggiunge un metodo proprio: area().
  • Quando creiamo un oggetto ret della sottoclasse Rettangoli, possiamo usare tutti i metodi della superclasse e anche quelli nuovi.Un rombo, per definizione, ha tutti i lati uguali, quindi può essere rappresentato come sottoclasse della classe Parallelogrammi, ma con un comportamento specifico.

Overriding (Ridefinizione di un metodo)

Definizione: Si ha overriding quando un metodo della classe base viene ridefinito in una sottoclasse con la stessa intestazione ma un comportamento diverso. Nel nostro esempio, la sottoclasse Rombi ridefinisce il metodo perimetro(), perché nel rombo il perimetro si calcola come 4 * lato1 (visto che tutti i lati sono uguali), mentre nella classe base si usava la formula generale per i parallelogrammi.

Codice con Rombi e overriding

class Parallelogrammi: # classe base def_init_(self, 11, 12, lug): self.lato1 = l1 self.lato2 = l2 self.lati_uguali = lug def perimetro(self): return (self.lato1 + self.lato2) * 2 def descrizione(self): if self.lati_uguali == True: print("il quadrilatero è un rombo") else: print("è un generico parallelogramma") class Rombi(Parallelogrammi): # sottoclasse def_init_(self, 11, 12=1, lug=True): # valori di default Parallelogrammi ._ init_(self, 11, 12, lug) def perimetro(self): # overriding del metodo return self.lato1 * 4 # Programma principale lato_r = float(input('inserisci il lato del rombo: ')) print("creo un oggetto della sottoclasse:") rom = Rombi(lato_r) rom.descrizione() print('il perimetro misura:', rom.perimetro())

Risultato atteso (con input 5.32)

inserisci il lato del rombo: 5.32 creo un oggetto della sottoclasse: il quadrilatero è un rombo il perimetro misura: 21.28

Approfondimento

  • Rombi eredita da Parallelogrammi, ma ridefinisce perimetro(), usando la sua formula specifica.
  • Anche il costruttore è adattato: dato che un rombo ha un solo lato caratteristico, 12 è opzionale (=1) e lati_uguali è True per impostazione predefinita.
  • La chiamata a Parallelogrammi ._ init_() serve per inizializzare correttamente gli attributi ereditati.

La parola chiave pass

In Python, si usa la parola chiave pass quando si vuole creare una sottoclasse ma non si ha ancora bisogno di aggiungere nulla di nuovo rispetto alla superclasse. Esempio: class Parallelogrammi: def_init_(self, 11, 12): self.lato1 = l1 self.lato2 = l2 def perimetro(self): return (self.lato1 + self.lato2) * 2 class Rettangoli(Parallelogrammi): pass # eredita tutto senza aggiunte # Uso r = Rettangoli(4, 6) print("perimetro:", r.perimetro()) # Usa il metodo ereditato dalla superclasse Rettangoli eredita tutto da Parallelogrammi, senza ridefinire nulla. Il pass serve a dire "non aggiungo altro, ma la classe esiste".

Diagramma UML riassuntivo

Ecco un diagramma UML creato apposta per te, che rappresenta le relazioni di ereditarietà tra le tre classi: · Parallelogrammi è la superclasse · Rettangoli e Rombi sono sottoclassi Parallelogrammi classe base o superclasse attributi metodi () A Rettangoli Rombi attributi attributi metodi () classi derivate o sottoclassi metodi ()

Il metodo super()

Il metodo super() di Python semplifica la gestione dei meccanismi dell'ereditarietà e dell'overriding. Una caratteristica utile di super() è che consente di riferirsi alla superclasse senza nominarla esplicitamente, evitando ridondanze. Ad esempio, al posto di: Parallelogrammi ._ init_(self, 11, 12, lug) si può scrivere: super() ._ init_(11, 12, lug) Inoltre, si usa super() anche per ridefinire i metodi (overriding) e mantenere l'accesso ai metodi originali della superclasse.

Esempio d'uso del metodo super()

class Rettangoli: def _init_(self, b, a): self.base = b self.altezza = a def area(self): return self.base * self.altezza def perimetro(self): return (self.base + self.altezza) * 2 class Quadrati(Rettangoli): def _init_(self, I): # solo un argomento super() ._ init_(I, I) lato_q = float(input("inserisci il lato del quadrato: ")) print("creo un oggetto della sottoclasse:") quad = Quadrati(lato_q) print("l'area misura", quad.area()) print("il perimetro misura", quad.perimetro()) Grazie a super(), la sottoclasse Quadrati può utilizzare i metodi della superclasse passando un solo parametro, che viene duplicato per simulare base e altezza.

Ripassa i concetti-chiave

  • L'ereditarietà consente a una sottoclasse di riutilizzare attributi e metodi di una superclasse.
  • Le sottoclassi possono aggiungere o ridefinire attributi/metodi (overriding).
  • super() permette di accedere alla superclasse senza ripeterne il nome.
  • Le classi astratte (ABC) sono template che impongono alle sottoclassi di implementare certi metodi.
  • Il polimorfismo consente a oggetti diversi di rispondere in modo diverso allo stesso metodo, a seconda della posizione nella gerarchia.
  • Le gerarchie complesse permettono la presenza di sotto-sottoclassi e classi con più livelli di ereditarietà.

L'ereditarietà multipla

Testo trascritto: Si possono anche presentare situazioni in cui i membri necessari per una sottoclasse non sono derivabili da un'unica superclasse. Per questi casi il linguaggio Python permette di introdurre un'ereditarietà multipla, ossia di far derivare una sottoclasse da più superclassi. In questo modo, una sottoclasse può combinare funzionalità provenienti da diverse classi base. Tuttavia, è importante gestire con attenzione i possibili conflitti tra metodi omonimi ereditati da superclassi diverse, per evitare ambiguità: Python risolve questi conflitti seguendo un ordine chiamato MRO (Method Resolution Order), che stabilisce la gerarchia di ricerca dei metodi.

Esempio Pratico di Ereditarietà Multipla in Python

L'ereditarietà multipla permette a una classe di ereditare attributi e metodi da più di una classe base. Questo può essere utile quando una classe deve combinare funzionalità provenienti da diverse sorgenti. Esempio: class Volante: def_init_(self, sa_volare=True): self.sa_volare = sa_volare def descrivi_volo(self): return "Può volare" if self.sa_volare else "Non può volare" class Nuotatore: def_init_(self, sa_nuotare=True): self.sa_nuotare = sa_nuotare def descrivi_nuoto(self): return "Può nuotare" if self.sa_nuotare else "Non può nuotare" class Pinguino(Volante, Nuotatore): def _init_(self): Volante ._ init_(self, sa_volare=False) Nuotatore ._ init_(self, sa_nuotare=True) def descrivi(self): volo = self.descrivi_volo() nuoto = self.descrivi_nuoto() return f"Pinguino: {volo}, {nuoto}" # Utilizzo pingu = Pinguino() print(pingu.descrivi()) Output: Pinguino: Non può volare, Può nuotare In questo esempio, la classe Pinguino eredita sia da Volante che da Nuotatore. Questo le consente di combinare comportamenti da entrambe le classi base, specificando che i pinguini non possono volare ma possono nuotare.

Classe base: MezzoDiTrasporto

class MezzoDiTrasporto: def_init_(self, velocita_media, consumo_km=0.0, carburante="Nessuno", infrastruttura=False): self.velocita_media = velocita_media self.consumo_km = consumo_km self.carburante = carburante self.infrastruttura = infrastruttura def calcola_durata(self, distanza): return distanza / self.velocita_media def calcola_consumo(self, distanza): return distanza * self.consumo_km Questa è la classe base che definisce le proprietà e i metodi comuni a tutti i mezzi di trasporto. Include attributi come velocità media, consumo per chilometro, tipo di carburante e necessità di infrastruttura. I metodi permettono di calcolare la durata di un viaggio e il consumo di carburante.

Sottoclasse: VeicoloAMotore

class VeicoloAMotore(MezzoDiTrasporto): def_init_(self, velocita_media, consumo_km, carburante, cilindrata, numero_cilindri): # Inizializza gli attributi della classe madre super() ._ init_(velocita_media, consumo_km, carburante, infrastruttura=True) # Ora inizializza gli attributi specifici self.cilindrata = cilindrata self.numero_cilindri = numero_cilindri self.giri_motore = 0 def avvia(self): print("Motore avviato.") def spegni(self): print("Motore spento.") def aumenta_giri(self, valore): self.giri_motore += valore print(f"Giri motore: {self.giri_motore}") Questa sottoclasse estende MezzoDiTrasporto aggiungendo caratteristiche specifiche dei veicoli a motore. Oltre agli attributi ereditati, include cilindrata, numero di cilindri e giri del motore. Aggiunge anche metodi per avviare, spegnere e aumentare i giri del motore.

  1. La prima riga è il costruttore della sottoclasse VeicoloAMotore Serve a raccogliere tutti i dati necessari per creare un oggetto di tipo VeicoloAMotore: velocita_media, consumo_km, carburante >> appartengono alla classe madre MezzoDiTrasporto cilindrata, numero_cilindri > sono specifici di VeicoloAMotore
  2. Poi usi super().init( ... ) per passare i valori alla classe madre Con questa riga stai dicendo: "Ehi, classe base MezzoDiTrasporto, inizializza tu i tuoi attributi (velocita_media, consumo_km, carburante, infrastruttura)" È come dire: "lo, classe figlia, mi occupo solo dei miei attributi speciali, ma per tutto il resto chiamo la mamma!"

Non hai trovato quello che cercavi?

Esplora altri argomenti nella Algor library o crea direttamente i tuoi materiali con l’AI.