Vai al contenuto
eLearner.app
Modulo 7 · Lezione 3 di 427/36 nel corso~12 min
Lezioni del modulo (3/4)

Metodi speciali (dunder)

I metodi speciali (chiamati dunder methods perché iniziano e finiscono con doppio underscore: __nome__) sono i ganci che permettono ai tuoi oggetti di integrarsi con il linguaggio: rappresentazione testuale, confronto con ==, supporto a len(), in, operatori, ecc.

Ne hai già usato uno: __init__.

__str__ e __repr__

Controllano cosa appare con print() e nel REPL.

Python
class Punto:
    def __init__(self, x, y):
        self.x = x
        self.y = y

    def __repr__(self):
        return f"Punto({self.x}, {self.y})"

    def __str__(self):
        return f"({self.x}, {self.y})"

p = Punto(3, 4)
str(p)        # '(3, 4)'        — per umani
repr(p)       # 'Punto(3, 4)'   — per debugger / dev
print(p)      # '(3, 4)'
p             # Punto(3, 4)     in REPL → repr
[p, p]        # [Punto(3, 4), Punto(3, 4)]   — sempre repr nei contenitori

Se definisci solo __repr__, Python lo usa anche come fallback per __str__.

__eq__: ridefinire ==

Di default a == b confronta gli identificatori degli oggetti (is), non i contenuti. Per il confronto strutturale, definisci __eq__:

Python
class Punto:
    def __init__(self, x, y):
        self.x, self.y = x, y

    def __eq__(self, other):
        if not isinstance(other, Punto):
            return NotImplemented
        return (self.x, self.y) == (other.x, other.y)

Punto(1, 2) == Punto(1, 2)    # True
Punto(1, 2) == Punto(3, 4)    # False

Restituire NotImplemented (parola chiave speciale, non False!) quando i tipi non sono confrontabili è la prassi corretta: Python prova allora con l'__eq__ dell'altro operando.

__len__: supporto a len()

Python
class Cesto:
    def __init__(self):
        self.frutti = []

    def __len__(self):
        return len(self.frutti)

c = Cesto()
c.frutti.append("mela")
len(c)        # 1
bool(c)       # True (len > 0)

Definendo __len__, ottieni anche il boolean "gratis": un oggetto con len() == 0 diventa falsy.

Altri dunder utili (cenni)

  • __add__(self, other) → ridefinisce +
  • __lt__, __le__, __gt__, __ge__ → operatori di confronto
  • __contains__(self, item) → supporta item in self
  • __iter__ + __next__ → rende l'oggetto iterabile
  • __getitem__(self, key) → supporta self[key]
  • __hash__ → necessario per essere chiave di dict / elemento di set

Dunder repr vs str

C'è una differenza sottile tra questi due metodi:

  • __str__ è pensato per essere leggibile per l'utente finale (prosa amichevole).
  • __repr__ è pensato per essere univoco ed esplicito per lo sviluppatore (dovrebbe assomigliare al codice Python necessario a ricreare l'oggetto). Se __str__ non è definito, Python userà __repr__ come fallback.

Dunder repr vs str

C'è una differenza sottile tra questi due metodi:

  • __str__ è pensato per essere leggibile per l'utente finale (prosa amichevole).
  • __repr__ è pensato per essere univoco ed esplicito per lo sviluppatore (dovrebbe assomigliare al codice Python necessario a ricreare l'oggetto). Se __str__ non è definito, Python userà __repr__ come fallback.

Prova tu

Esercizio#python.m7.l3.e1
Tentativi: 0Caricamento…

Definisci `Money` con __init__(self, amount, currency) e __str__ che restituisce `f'{amount} {currency}'`. Crea `s = Money(42, 'EUR')` e valuta `str(s)`.

Caricamento editor…
Mostra suggerimento

f-string nel __str__.

Soluzione disponibile dopo 3 tentativi

Esercizio di ripasso

Esercizio#python.m7.l3.e2
Tentativi: 0Caricamento…

Definisci `Pair` con __init__(self, a, b) e __eq__ basato sul confronto strutturale di (a, b). Valuta `Pair(1, 2) == Pair(1, 2)`.

Caricamento editor…
Mostra suggerimento

Confronta le tuple (self.a, self.b) == (other.a, other.b).

Soluzione disponibile dopo 3 tentativi

Sfida aggiuntiva

Esercizio#python.m7.l3.e3
Tentativi: 0Caricamento…

Definisci una classe `Book` con costruttore che accetta `title` ed `author`. Definisci il metodo speciale `__str__(self)` affinché restituisca la stringa `"Titolo by Autore"` (es. `'1984 by George Orwell'`). Istanzia un libro ed evalua `str(book)`.

Caricamento editor…
Mostra suggerimento

Usa f"{self.title} by {self.author}" all'interno di def __str__(self):.

Soluzione disponibile dopo 3 tentativi