8.1 Polimorfismo
Il polimorfismo è uno dei concetti fondamentali della programmazione orientata agli oggetti (OOP), che consente agli oggetti di classi diverse di utilizzare la stessa interfaccia.
In Python, il polimorfismo viene raggiunto attraverso il dynamic typing e l'ereditarietà. Un aspetto importante del polimorfismo è l'overloading dei metodi e la loro sostituzione dai metodi della classe derivata.
Concetti principali del polimorfismo:
Interfaccia unica per oggetti diversi:
Il polimorfismo consente di utilizzare un'interfaccia unica per oggetti di classi diverse, che possono avere diverse implementazioni dei metodi.
Tipizzazione dinamica:
In Python, il tipo di una variabile viene determinato durante l'esecuzione, il che permette alle funzioni di lavorare con oggetti di qualsiasi classe, purché supportino l'interfaccia richiesta.
Ereditarietà:
Permette di creare gerarchie di classi, dove le sottoclassi ereditano proprietà e metodi della classe base, ma possono ridefinirli per la loro funzionalità specifica.
Esempi di polimorfismo:
Un semplice esempio di polimorfismo: tutte e tre le classi hanno un metodo chiamato
move()
. Questo significa che possiamo scrivere un codice che funzioni contemporaneamente con questi oggetti.
class Car:
def move(self):
pass
class Human:
def move(self):
pass
class Bird:
def move(self):
print("Car!")
car = Car()
human = Human()
bird = Bird()
for it in [car, human, bird]:
it.move()
In questo caso, "interfaccia comune" è il nome del metodo, o la sua signature, se il metodo ha parametri.
8.2 Ridefinizione dei metodi
Il polimorfismo viene spesso utilizzato insieme all'ereditarietà per creare gerarchie di classi, dove la classe base definisce un'interfaccia comune e le sottoclassi implementano dettagli specifici.
class Employee:
def get_salary(self):
return 1000
class FullTimeEmployee(Employee):
def get_salary(self):
return 5000
class PartTimeEmployee(Employee):
def get_salary(self):
return 3000
class Intern(Employee):
pass
def print_salary(employee):
print(employee.get_salary())
employees = [FullTimeEmployee(), PartTimeEmployee(), Intern()]
for employee in employees:
print_salary(employee)
Questo esempio è simile al precedente, ma ha una differenza importante: le nostre classi non devono
avere il metodo get_salary
, dal momento che esiste sempre nella classe base. Ora
l'"interfaccia comune" di tutte le classi non è semplicemente il metodo get_salary()
, ma
proprio la classe Employee
con tutti i suoi metodi e attributi.
8.3 Chiamata ai metodi della classe derivata
Dai un'occhiata al codice seguente:
Chiamiamo il metodo print_salary()
, che
esiste solo nella classe base Employee, e questa dalla classe base chiama
un altro metodo della classe base - get_salary()
. Quindi, quali stipendi verranno
stampati a video?
class Employee:
def print_salary(self):
salary = self.get_salary()
print(salary)
def get_salary(self):
return 1000
class FullTimeEmployee(Employee):
def get_salary(self):
return 5000
class PartTimeEmployee(Employee):
def get_salary(self):
return 3000
class Intern(Employee):
pass
employees = [FullTimeEmployee(), PartTimeEmployee(), Intern()]
for employee in employees:
employee.print_salary()
Importante!
Leggi quanto segue - è importante.
Se chiamiamo il metodo print_salary()
, per esempio, sulla classe FullTimeEmployee
, verrà
chiamato il metodo della classe base, poiché tale metodo non è dichiarato
nella classe stessa.
Ma questo metodo print_salary()
chiamerà il metodo get_salary()
sull'oggetto
self
, che ha il tipo FullTimeEmployee
, non Employee
. Quindi verrà
chiamato proprio il metodo get_salary()
della classe FullTimeEmployee
!
Inoltre, la classe FullTimeEmployee
può chiamare il metodo get_salary()
della classe base
per le sue necessità. Ad esempio, possiamo voler calcolare lo stipendio in percentuale
rispetto alla base:
class FullTimeEmployee(Employee):
def get_salary(self):
base_salary = super().get_salary()
return base_salary * 5 # 500%
class PartTimeEmployee(Employee):
def get_salary(self):
base_salary = super().get_salary()
return base_salary * 3 # 300%
8.4 Sovraccarico dei metodi
Il sovraccarico dei metodi (method overloading)
in Python è la capacità di creare
più metodi con lo stesso nome, ma con parametri diversi. Tuttavia, il sovraccarico
dei metodi non è supportato in modo diretto in Python, come avviene in altri linguaggi
(ad esempio, C++ o Java).
In Python si può simulare il sovraccarico dei metodi attraverso l'uso di argomenti
di default, *args
e **kwargs
.
Esempi di sovraccarico dei metodi
Possiamo realizzare una funzione che avrà comportamenti diversi a seconda del numero di argomenti passati. Questo si può fare in diversi modi, per esempio così:
class Example:
def display(self, a=None, b=None):
if a is not None and b is not None:
print(a, b)
elif a is not None:
print(a)
else:
print("Nessun argomento")
obj = Example()
obj.display(1, 2) # Output: 1 2
obj.display(1) # Output: 1
obj.display() # Output: Nessun argomento
Possiamo anche considerare il tipo di dati passato - semplicemente facendo un controllo sul tipo:
class Example:
def mod(self, a, b):
if type(a) == int and type(b) == int:
print(a % b)
elif type(a) == float or type(b) == float:
print(round(a / b))
else:
print("Istruzione di aiuto: a e b devono essere int o float")
obj = Example()
obj.mod(5, 2) # Output: 1
obj.mod(5.0, 2) # Output: 2
obj.mod("5", 2) # Output: Istruzione di aiuto: a e b devono essere int o float
Se vengono passati tipi di dati errati, è possibile visualizzare un'istruzione o anche un link alla documentazione online - anche questa è una soluzione molto popolare.
GO TO FULL VERSION