In questa lezione impari a
- creare una classe con attributi e metodi
- proteggere i dati con l'incapsulamento
- riusare il codice con l'ereditarietà
- capire il polimorfismo
Finora hai tenuto separati i dati (variabili, liste, dizionari) e le operazioni (funzioni). La programmazione a oggetti (OOP, Object Oriented Programming) li mette insieme: un oggetto contiene sia i suoi dati sia le azioni che sa fare.
Classi e oggetti
Una classe è il progetto, un oggetto è una cosa costruita da quel progetto. La classe Auto descrive com'è fatta un'auto; la tua Panda e la Golf del vicino sono due oggetti di quella classe.
class Persona:
def __init__(self, nome, eta):
self.nome = nome # attributi: i dati dell'oggetto
self.eta = eta
def presentati(self): # metodo: un'azione dell'oggetto
print(f"Ciao, sono {self.nome} e ho {self.eta} anni")
anna = Persona("Anna", 16) # crea un oggetto
luca = Persona("Luca", 17)
anna.presentati()
luca.presentati()
print(anna.nome)
__init__è il costruttore: viene eseguito quando crei l'oggetto e ne imposta i dati iniziali.selfindica l'oggetto stesso:self.nomeè "il nome di questo oggetto". Va sempre messo come primo parametro dei metodi.
Incapsulamento: proteggere i dati
Alcuni dati non devono essere modificati liberamente. Il saldo di un conto in banca, per esempio, deve cambiare solo con versamenti e prelievi controllati.
| Scrittura | Significato |
|---|---|
self.nome |
pubblico: accessibile da tutti |
self._nome |
protetto: per convenzione, "non toccare da fuori" |
self.__nome |
privato: Python lo nasconde davvero |
class ContoBancario:
def __init__(self, titolare):
self.titolare = titolare
self.__saldo = 0
def versa(self, importo):
if importo <= 0:
print("Importo non valido")
return
self.__saldo += importo
def preleva(self, importo):
if importo > self.__saldo:
print("Saldo insufficiente")
return
self.__saldo -= importo
def saldo(self):
return self.__saldo
conto = ContoBancario("Marta")
conto.versa(100)
conto.preleva(150) # Saldo insufficiente
print(conto.saldo()) # 100
Dall'esterno nessuno può scrivere conto.__saldo = 1000000: il saldo cambia solo attraverso i metodi, che controllano che l'operazione sia valida.
Ereditarietà: riusare il codice
Una classe può ereditare da un'altra: riceve tutti i suoi attributi e metodi e può aggiungerne di nuovi.
class Studente(Persona):
def __init__(self, nome, eta, classe):
super().__init__(nome, eta) # usa il costruttore di Persona
self.classe = classe
def presentati(self):
super().presentati()
print(f"Frequento la {self.classe}")
marta = Studente("Marta", 16, "3B")
marta.presentati()
Studente è una sottoclasse di Persona. super() richiama i metodi della classe "genitore".
Polimorfismo: stesso metodo, comportamenti diversi
Classi diverse possono avere un metodo con lo stesso nome, ciascuna con il suo comportamento.
class Animale:
def __init__(self, nome):
self.nome = nome
def verso(self):
return "..."
class Cane(Animale):
def verso(self):
return "Bau!"
class Gatto(Animale):
def verso(self):
return "Miao!"
class Mucca(Animale):
def verso(self):
return "Muu!"
fattoria = [Cane("Fido"), Gatto("Micia"), Mucca("Carolina")]
for animale in fattoria:
print(f"{animale.nome}: {animale.verso()}")
Il ciclo non deve sapere che tipo di animale sta trattando: chiama verso() e ciascuno risponde a modo suo.
Esercizi
Esercizio 1 – La classe Studente. Crea una classe Studente con nome e una lista di voti, un metodo aggiungi_voto(voto) e un metodo media().
Mostra la soluzione
class Studente:
def __init__(self, nome):
self.nome = nome
self.voti = []
def aggiungi_voto(self, voto):
if 1 <= voto <= 10:
self.voti.append(voto)
def media(self):
if not self.voti:
return 0
return sum(self.voti) / len(self.voti)
s = Studente("Ivan")
s.aggiungi_voto(7)
s.aggiungi_voto(9)
print(f"{s.nome}: media {s.media():.1f}") # Ivan: media 8.0
Esercizio 2 – L'auto. Crea una classe Auto con marca, modello e chilometri. Aggiungi un metodo viaggia(km) che aumenta i chilometri (rifiutando valori negativi) e un metodo scheda() che stampa i dati.
Mostra la soluzione
class Auto:
def __init__(self, marca, modello, km=0):
self.marca = marca
self.modello = modello
self.km = km
def viaggia(self, km):
if km < 0:
print("I chilometri non possono essere negativi")
return
self.km += km
def scheda(self):
print(f"{self.marca} {self.modello} - {self.km} km")
panda = Auto("Fiat", "Panda", 12000)
panda.viaggia(350)
panda.scheda() # Fiat Panda - 12350 km
Sfida: il garage virtuale
Crea una classe Garage che contiene una lista di oggetti Auto, con i metodi parcheggia(auto), ritira(modello), elenco() e km_totali(). Poi aggiungi una sottoclasse AutoElettrica con l'autonomia della batteria e un metodo ricarica().