Fondamenti di Python

Programmazione orientata agli oggetti in Python

La OOP organizza un programma attorno a oggetti che possiedono stato ed espongono comportamenti tramite interfacce chiare.

Cos’è la programmazione orientata agli oggetti in Python?

La OOP modella un sistema come oggetti che collaborano. Ogni oggetto unisce stato e operazioni consentite, chiarendo le responsabilità nelle applicazioni grandi.

I quattro principi fondamentali

  • Incapsulamento: riunire stato e comportamento e controllarne l’accesso.
  • Astrazione: mostrare un’interfaccia utile nascondendo i dettagli superflui.
  • Ereditarietà: specializzare una classe solo per una vera relazione is-a.
  • Polimorfismo: usare la stessa operazione con oggetti diversi e comportamento appropriato.

Progettazione dell’esempio

  1. BankAccount possiede il saldo e valida i depositi.
  2. SavingsAccount riutilizza il contratto di base.
  3. SavingsAccount ridefinisce month_end per applicare gli interessi.
  4. Il ciclo usa tutti i conti tramite la stessa interfaccia.
Suggerimento: Preferisci la composizione quando un oggetto ne usa un altro; usa l’ereditarietà solo con sostituzione corretta.

Esempio di base

Esegui codice →
main.py
class BankAccount:
    def __init__(self, owner, balance=0):
        self.owner = owner
        self._balance = float(balance)

    @property
    def balance(self):
        return self._balance

    def deposit(self, amount):
        if amount <= 0:
            raise ValueError("Deposit must be positive")
        self._balance += amount

    def month_end(self):
        return "No interest"


class SavingsAccount(BankAccount):
    def __init__(self, owner, balance, interest_rate):
        super().__init__(owner, balance)
        self.interest_rate = interest_rate

    def month_end(self):
        self._balance *= 1 + self.interest_rate
        return "Interest applied"


accounts = [
    BankAccount("Ada", 100),
    SavingsAccount("Lin", 200, 0.05),
]
accounts[0].deposit(50)

for account in accounts:
    status = account.month_end()
    print(f"{account.owner}: {account.balance:.2f} ({status})")

Output previsto

Ada: 150.00 (No interest)
Lin: 210.00 (Interest applied)

I quattro principi OOP in pratica

Questi esempi mirati mostrano come Python applica incapsulamento, astrazione, ereditarietà e polimorfismo nel codice quotidiano.

Incapsulamento

Esegui codice →

riunire stato e comportamento e controllarne l’accesso.

encapsulation.py
class BankAccount:
    def __init__(self, balance=0):
        # Double underscores prevent accidental direct access.
        self.__balance = balance

    @property
    def balance(self):
        # Expose read access without exposing mutation.
        return self.__balance

    def deposit(self, amount):
        # Keep validation next to the state it protects.
        if amount <= 0:
            raise ValueError("Amount must be positive")
        self.__balance += amount


account = BankAccount(100)
account.deposit(50)
print(account.balance)  # 150

Astrazione

Esegui codice →

mostrare un’interfaccia utile nascondendo i dettagli superflui.

abstraction.py
from abc import ABC, abstractmethod


class PaymentMethod(ABC):
    @abstractmethod
    def pay(self, amount):
        # Callers know what pay does, not how each provider does it.
        pass


class CreditCard(PaymentMethod):
    def pay(self, amount):
        # The concrete class hides its payment implementation.
        return f"Paid ${amount:.2f} by card"


payment = CreditCard()
print(payment.pay(25))

Ereditarietà

Esegui codice →

specializzare una classe solo per una vera relazione is-a.

inheritance.py
class Employee:
    def __init__(self, name):
        self.name = name

    def describe(self):
        return self.name


class Developer(Employee):
    def __init__(self, name, language):
        # Reuse initialization from the parent class.
        super().__init__(name)
        self.language = language

    def describe(self):
        # Extend inherited behavior instead of duplicating it.
        return f"{super().describe()} writes {self.language}"


print(Developer("Ada", "Python").describe())

Polimorfismo

Esegui codice →

usare la stessa operazione con oggetti diversi e comportamento appropriato.

polymorphism.py
class EmailNotification:
    def send(self, message):
        return f"Email: {message}"


class SmsNotification:
    def send(self, message):
        return f"SMS: {message}"


def notify(channels, message):
    for channel in channels:
        # The same call selects behavior from each concrete object.
        print(channel.send(message))


channels = [EmailNotification(), SmsNotification()]
notify(channels, "Order shipped")

Come funziona

BankAccount incapsula il saldo. SavingsAccount eredita l’interfaccia e ridefinisce month_end. Il ciclo usa entrambi gli oggetti allo stesso modo e Python seleziona il metodo a runtime.

Modifica i valori ed esegui il programma nel compilatore Python online CodeUtility senza installare Python.

Esercizi pratici

Modifica gli input e verifica i casi limite prima di usare dataset più grandi.

  1. Prevedi l’output prima dell’esecuzione.
  2. Gestisci input vuoti o non validi.
  3. Sposta la logica in una funzione e aggiungi test.
Esegui nell’IDE Python →