Lezioni del modulo (2/4)
Ereditarietà
L'ereditarietà ti permette di creare una nuova classe (la sottoclasse) che eredita attributi e metodi da una classe esistente (la classe base), e li può estendere o sovrascrivere.
Sintassi base
class Animale:
def __init__(self, nome):
self.nome = nome
def saluta(self):
return f"{self.nome} fa un verso"
class Cane(Animale): # Cane EREDITA da Animale
def saluta(self): # OVERRIDE del metodo
return f"{self.nome} dice WOOF!"
a = Animale("X")
c = Cane("Fido")
a.saluta() # 'X fa un verso'
c.saluta() # 'Fido dice WOOF!'Cane eredita __init__ da Animale senza riscriverlo.
super(): chiamare il metodo della base
Quando vuoi estendere un metodo della base (non sostituirlo
completamente), usa super():
class Animale:
def __init__(self, nome):
self.nome = nome
class Cane(Animale):
def __init__(self, nome, razza):
super().__init__(nome) # delega alla base
self.razza = razza
c = Cane("Fido", "Labrador")
c.nome # 'Fido'
c.razza # 'Labrador'isinstance: controllo del tipo
isinstance(c, Cane) # True
isinstance(c, Animale) # True (anche la base)
isinstance(c, str) # Falseisinstance(x, ClasseBase) riconosce anche tutte le sottoclassi: questo è
ciò che rende l'ereditarietà utile per il polimorfismo.
animali = [Cane("Fido", "X"), Animale("Pippo")]
for a in animali:
print(a.saluta()) # ogni oggetto risponde a suo modoMRO (Method Resolution Order) — cenni
Quando hai ereditarietà multipla, Python decide in che ordine cercare un metodo seguendo l'MRO, calcolato con l'algoritmo C3. Per il 95% dei casi basta sapere:
- cerca prima nell'istanza,
- poi nella sua classe,
- poi nelle classi base in ordine di dichiarazione.
Cane.__mro__
# (<class 'Cane'>, <class 'Animale'>, <class 'object'>)object è la radice di ogni classe in Python 3.
Ereditarietà multipla e MRO
A differenza di altri linguaggi (es. Java o C#), Python supporta l'ereditarietà multipla: una classe può ereditare contemporaneamente da più classi madri. L'ordine di risoluzione dei metodi viene gestito tramite un algoritmo chiamato MRO (Method Resolution Order), verificabile richiamando MiaClasse.mro().
Prova tu
Data `class Vehicle: def __init__(self, brand): self.brand = brand`, definisci `Car(Vehicle)` con __init__(self, brand, model) che chiama super().__init__ e salva self.model. Crea `a = Car('Fiat', '500')` e valuta `(a.brand, a.model)`.
Mostra suggerimento
super().__init__(brand) poi self.model = model.
Soluzione disponibile dopo 3 tentativi
Esercizio di ripasso
Definisci `class Shape: def area(self): return 0` e `class Square(Shape)` con __init__(self, side) e area() che restituisce side * side. Crea `q = Square(5)` e valuta la tupla `(q.area(), isinstance(q, Shape))`.
Mostra suggerimento
self.side * self.side.
Soluzione disponibile dopo 3 tentativi
Sfida aggiuntiva
Crea una classe `Square` che erediti dalla classe `Rectangle` (definita sotto). Il costruttore di `Square` deve accettare un singolo parametro `side` e passarlo a `super().__init__(side, side)`. Istanzia un quadrato con lato `6` salvandolo in `sq` e valuta `sq.area()`.
Mostra suggerimento
Dichiara class Square(Rectangle): e chiama super().__init__(side, side) nel costruttore.
Soluzione disponibile dopo 3 tentativi