OOP Avanzata: Ereditarietà, Classi Astratte e Polimorfismo
Obiettivi della pillola
In questa seconda parte approfondita imparerai a:
- Definire gerarchie di classi con l’Ereditarietà.
- Usare le Classi Astratte per imporre regole alle sottoclassi.
- Utilizzare i Metodi Statici per utility slegate dall’istanza.
- Implementare l’Overloading degli Operatori per confrontare oggetti (es.
<,==).
Classi Astratte ed Ereditarietà
Spesso vogliamo definire un “modello base” che non può essere usato direttamente, ma serve solo come fondamenta per classi più specifiche.
Il modulo abc (Abstract Base Classes)
Immaginiamo di voler gestire sia indirizzi IPv4 che IPv6. Creiamo una classe genitore IPAddress che obbliga i figli a implementare certi metodi.
Gerarchia delle classi:
from abc import ABC, abstractmethod
# Classe Astratta (eredita da ABC)
class IPAddress(ABC):
def __init__(self, address):
self.address = address
# Metodo Astratto: i figli DEVONO implementarlo
@abstractmethod
def to_binary(self):
pass
# Metodo Concreto: i figli lo ereditano gia pronto
def __str__(self):
return f"IP: {self.address}"
# Sottoclasse Concreta
class IPv4(IPAddress):
def to_binary(self):
# Logica specifica per IPv4
ottetti = self.address.split('.')
binario = [format(int(x), '08b') for x in ottetti]
return "".join(binario)
Se provassi a istanziare IPAddress direttamente o a creare una sottoclasse senza to_binary, Python solleverebbe un errore.
Metodi Statici (@staticmethod)
A volte serve una funzione che logicamente appartiene alla classe, ma che non ha bisogno di accedere a self (cioè ai dati dell’istanza). Si usano spesso per funzioni di validazione o utility.
class IPv4(IPAddress):
# ... metodi precedenti ...
@staticmethod
def is_valid_format(ip_str):
"""Controlla se una stringa sembra un IPv4 valido."""
parts = ip_str.split('.')
if len(parts) != 4:
return False
for p in parts:
if not p.isdigit() or not (0 <= int(p) <= 255):
return False
return True
# Possiamo chiamarlo SENZA creare un oggetto!
print(IPv4.is_valid_format("192.168.1.1")) # True
print(IPv4.is_valid_format("300.0.0.1")) # False
Overloading degli Operatori e Comparatori
In Python non esiste l’overloading dei metodi (stesso nome, parametri diversi) come in Java, ma è potentissimo l’Overloading degli Operatori. Possiamo ridefinire come gli oggetti reagiscono agli operatori standard (+, -, ==, <) usando i Magic Methods.
Comparazione (__eq__, __lt__)
Vogliamo poter ordinare una lista di IP o chiedere if ip1 > ip2.
class IPv4(IPAddress):
# ...
def __eq__(self, other):
"""Definisce il comportamento dell'operatore =="""
if not isinstance(other, IPv4):
return False
return self.address == other.address
def __lt__(self, other):
"""Definisce il comportamento dell'operatore < (Less Than)"""
# Trucco: confrontiamo i valori interi o le stringhe binarie
return self.to_binary() < other.to_binary()
# Utilizzo
ip_a = IPv4("10.0.0.1")
ip_b = IPv4("192.168.1.1")
if ip_a < ip_b:
print(f"{ip_a} viene prima di {ip_b}")
# Ordinamento automatico nelle liste!
lista_ip = [ip_b, ip_a]
lista_ip.sort() # Usa automaticamente __lt__
print([str(ip) for ip in lista_ip])
Overloading Operatori Matematici
Possiamo anche definire l’addizione. Ad esempio, sommare un intero a un IP per ottenere l’IP successivo.
def __add__(self, n):
"""Gestisce: nuovo_ip = oggetto_ip + intero"""
# 1. Converti IP in intero
# 2. Somma n
# 3. Riconverti in stringa e restituisci nuovo oggetto
# (Implementazione lasciata per esercizio)
pass
Riepilogo Concettuale
- Ereditarietà:
class B(A). “B è un tipo di A”. Serve a non riscrivere codice comune. - Classe Astratta: Una classe “incompleta” che definisce le regole per i figli.
- Metodo Statico: Funzione dentro una classe che non usa
self. - Overloading: Dare nuovi significati agli operatori
+,==,<per i nostri oggetti.
Esercitazione Avanzata
Estendi la classe IPv4 creata sopra:
- Implementa
__add__: Fai in modo cheIPv4("192.168.1.1") + 2restituisca un nuovo oggettoIPv4("192.168.1.3"). - Implementa
__sub__: La differenza tra due IP deve restituire il numero di host tra di essi (un intero). - Crea una classe
Networkche accetta un IP e una maschera, e usa un metodo per generare tutti gli IP utilizzabili (usando il tuo operatore+).
EC