🐍 Programmazione ad Oggetti (OOP) in Python

Scopri come strutturare il tuo codice in modo modulare, riutilizzabile e professionale con la programmazione orientata agli oggetti

📚 Perché usare la Programmazione ad Oggetti?

📦 Organizzazione

Il codice è suddiviso in moduli facilmente gestibili e comprensibili

♻️ Riutilizzabilità

Le classi possono essere riutilizzate in progetti diversi senza riscrivere codice

🔧 Manutenibilità

Facilita la modifica del codice senza intaccare altre parti del programma

🎭 Astrazione

Permette di nascondere i dettagli di implementazione, esponendo solo le funzionalità necessarie


1

Classe e Oggetto

Una classe è un modello (template) che definisce la struttura e il comportamento degli oggetti. Un oggetto è un’istanza concreta di quella classe, con i propri dati specifici.

💡 Analogia: La classe è come il progetto di una casa, mentre l’oggetto è la casa vera e propria costruita seguendo quel progetto.
Python
class Persona:
    def __init__(self, nome, eta):
        self.nome = nome  # Attributo pubblico
        self.eta = eta    # Attributo pubblico

    def saluta(self):
        return f"Ciao, mi chiamo {self.nome} e ho {self.eta} anni."

# Creazione di un oggetto (istanza della classe)
persona1 = Persona("Alice", 25)
print(persona1.saluta())
# Output: Ciao, mi chiamo Alice e ho 25 anni.

📋 Quando usarlo?

  • Quando vuoi creare strutture dati complesse con metodi associati
  • Per rappresentare entità del mondo reale (Utente, Auto, Studente, ecc.)
  • Quando hai bisogno di creare più istanze con la stessa struttura

2

Incapsulamento

L’incapsulamento è il principio che protegge lo stato interno di un oggetto, permettendo l’accesso ai dati solo attraverso metodi dedicati. Questo previene modifiche accidentali e mantiene l’integrità dei dati.

🔐 Livelli di accesso in Python:

  • Pubblico: accessibile ovunque (self.nome)
  • Protetto: accessibile solo dalla classe e dalle sottoclassi (self._nome)
  • Privato: offuscato e non accessibile direttamente (self.__nome)
Python
class ContoBancario:
    def __init__(self, titolare, saldo):
        self.titolare = titolare    # Attributo pubblico
        self.__saldo = saldo        # Attributo privato (protetto)

    def deposita(self, importo):
        if importo > 0:
            self.__saldo += importo
            return f"✅ Deposito di {importo}€ effettuato. Nuovo saldo: {self.__saldo}€"
        else:
            return "❌ Importo non valido."

    def preleva(self, importo):
        if 0 < importo <= self.__saldo:
            self.__saldo -= importo
            return f"✅ Prelievo di {importo}€ effettuato. Saldo residuo: {self.__saldo}€"
        else:
            return "❌ Fondi insufficienti o importo non valido."

    def mostra_saldo(self):
        return f"💰 Saldo disponibile: {self.__saldo}€"

# Utilizzo
conto = ContoBancario("Mario Rossi", 1000)
print(conto.deposita(500))
print(conto.preleva(200))
print(conto.mostra_saldo())

# Tentativo di accesso diretto (NON funziona)
# print(conto.__saldo)  # Genera errore!

📋 Quando usarlo?

  • Per proteggere dati sensibili (password, saldo bancario, informazioni personali)
  • Quando vuoi controllare come i dati vengono modificati
  • Per evitare modifiche accidentali alle variabili interne

3

Ereditarietà

L'ereditarietà consente di creare una nuova classe che eredita attributi e metodi da un'altra classe esistente. La classe da cui si eredita si chiama classe base (o superclasse), mentre la nuova classe è la sottoclasse.

💡 Vantaggi: Evita la duplicazione del codice e crea una gerarchia logica tra le classi. Puoi specializzare una classe generale aggiungendo nuove funzionalità.
Python
class Persona:
    def __init__(self, nome, eta):
        self.nome = nome
        self.eta = eta

    def saluta(self):
        return f"Ciao, mi chiamo {self.nome} e ho {self.eta} anni."

# Studente eredita da Persona
class Studente(Persona):
    def __init__(self, nome, eta, scuola):
        super().__init__(nome, eta)  # Chiama il costruttore della classe base
        self.scuola = scuola

    def info_scuola(self):
        return f"📚 Frequento {self.scuola}."

# Creazione di un oggetto della sottoclasse
studente1 = Studente("Marco", 18, "Liceo Scientifico")
print(studente1.saluta())        # Metodo ereditato ✅
print(studente1.info_scuola())   # Metodo specifico della sottoclasse ✅

📋 Quando usarlo?

  • Per evitare duplicazione del codice creando classi più generiche
  • Quando un oggetto ha caratteristiche comuni con un altro ma necessita di personalizzazioni
  • Per creare gerarchie logiche (es: Animale → Cane, Gatto)

4

Polimorfismo

Il polimorfismo permette a oggetti di classi diverse di essere trattati allo stesso modo se condividono un'interfaccia comune. In pratica, puoi chiamare lo stesso metodo su oggetti diversi e ottenere comportamenti specifici per ciascuno.

💡 Significato: "Poli" = molte, "morfismo" = forme. Un'unica interfaccia per molte implementazioni diverse.
Python
class Animale:
    def verso(self):
        pass  # Metodo da implementare nelle sottoclassi

class Cane(Animale):
    def verso(self):
        return "🐕 Bau bau!"

class Gatto(Animale):
    def verso(self):
        return "🐈 Miao!"

class Mucca(Animale):
    def verso(self):
        return "🐄 Muuu!"

# Uso polimorfico - stesso metodo, comportamenti diversi
animali = [Cane(), Gatto(), Mucca()]

for animale in animali:
    print(animale.verso())

# Output:
# 🐕 Bau bau!
# 🐈 Miao!
# 🐄 Muuu!

📋 Quando usarlo?

  • Quando più classi devono comportarsi in modo simile ma con implementazioni diverse
  • Per scrivere codice più generico che funzioni su tipi diversi senza modifiche
  • Per creare sistemi estensibili e flessibili

🎯 Riepilogo dei Concetti Fondamentali

📦 Classe e Oggetto

La struttura base della OOP. La classe definisce il modello, l'oggetto è l'istanza concreta.

🔐 Incapsulamento

Protegge i dati interni controllando l'accesso attraverso metodi specifici.

🧬 Ereditarietà

Riutilizza codice esistente creando classi specializzate da classi generiche.

🎭 Polimorfismo

Permette flessibilità: stesso metodo, comportamenti diversi per ogni classe.

Utilizzando questi principi, puoi scrivere codice più robusto, manutenibile e professionale! 🚀


📚 Approfondisci con Esempi Pratici

Torna in alto