Il duck typing è un principio fondamentale di Python, secondo il quale un oggetto è valutato in base al suo comportamento, piuttosto che in base all'appartenenza a una gerarchia di classi.
Il termine deriva dal detto: "Se qualcosa sembra un'anatra, nuota come un'anatra e quack come un'anatra — allora è un'anatra". In Python, il comportamento di un oggetto (la sua interfaccia) è più importante della classe a cui appartiene. Ciò realizza il principio del "duck typing" — tipizzazione basata sul comportamento (structural typing).
Sembra che il duck typing offra la massima flessibilità. Ma questo aumenta il numero di bug nascosti: il programma "si rompe" solo durante l'esecuzione, se l'oggetto non supporta l'interfaccia richiesta.
Invece di controllare il tipo (tramite isinstance o type), scrivi funzioni che provano a chiamare i metodi richiesti sull'oggetto, contando sul fatto che se li supporta — tutto funzionerà. In caso contrario, cattura AttributeError o TypeError per gestire oggetti inaspettati.
Esempio:
def quack_and_walk(duck): duck.quack() duck.walk() class Robot: def quack(self): print("Posso quack!") def walk(self): print("Sto camminando") quack_and_walk(Robot()) # tutto funzionerà!
Caratteristiche chiave:
È possibile in Python verificare il tipo tramite isinstance e dire che è corretto per il duck typing?
No. Il duck typing è proprio opposto a un rigoroso controllo dei tipi. È più corretto operare sul comportamento dell'oggetto piuttosto che sulla sua genealogia.
È possibile implementare duck typing utilizzando classi base astratte (ABC)?
Parzialmente. Le classi astratte introducono elementi di struttura statica. Il duck typing non richiede di dichiarare una parentela — è necessario semplicemente implementare i metodi richiesti. Ma con Python 3.8 è stato introdotto il modulo typing.Protocol, che ci avvicina alla tipizzazione strutturale.
Può il duck typing funzionare con metodi magici (len, getitem ecc.)?
Sì. Se un oggetto implementa il metodo richiesto (len), può essere passato a funzioni, ad esempio, len(obj), e questo funzionerà secondo il duck typing.
Uno sviluppatore scrive una funzione che accetta tutto con un metodo run(), senza verifica. Nel codice compare un oggetto senza quel metodo — e l'errore si verifica solo a runtime.
Pro:
Contro:
Utilizzare duck typing, ma con try/except e documentazione dell'interfaccia:
def run_task(obj): try: obj.run() except AttributeError: print("L'oggetto non supporta l'esecuzione del compito!")
Pro:
Contro: