El duck typing es un principio fundamental de Python, según el cual un objeto se considera por su comportamiento, no por su pertenencia a la jerarquía de clases.
El término proviene de la expresión: "Si algo tiene aspecto de pato, nada como un pato y grazna como un pato, entonces es un pato". En Python, el comportamiento de un objeto (su interfaz) es más importante que la clase a la que pertenece. Esto implementa el principio de "duck typing": tipificación por comportamiento (tipificación estructural).
Parece que el duck typing brinda la máxima flexibilidad. Pero esto aumenta la cantidad de errores ocultos: el programa "se rompe" solo durante la ejecución, si el objeto no soporta la interfaz necesaria.
En lugar de comprobar el tipo (a través de isinstance o type), escribe funciones que intenten llamar a los métodos necesarios del objeto, asumiendo que si los soporta, todo funcionará. En el peor de los casos, atrapa AttributeError o TypeError para manejar objetos inesperados.
Ejemplo:
def quack_and_walk(duck): duck.quack() duck.walk() class Robot: def quack(self): print("¡Puedo graznar!") def walk(self): print("Estoy caminando") quack_and_walk(Robot()) # ¡todo funcionará!
Características clave:
¿Se puede comprobar el tipo en Python a través de isinstance y decir que es correcto para el duck typing?
No. El duck typing se opone a la verificación rígida de tipos. Es más correcto manejar el comportamiento del objeto, no su linaje.
¿Se puede implementar duck typing mediante clases base abstractas (ABC)?
Parcialmente. Las clases abstractas introducen elementos de estructura estática. El duck typing no requiere declarar parentesco: simplemente debes implementar los métodos necesarios. Pero con Python 3.8 se introdujo el módulo typing.Protocol, que nos acerca a la tipificación estructural.
¿Puede el duck typing funcionar con métodos mágicos (len, getitem etc.)?
Sí. Si un objeto implementa el método necesario (len), se le puede pasar a funciones como len(obj), y funcionará según el duck typing.
Un desarrollador escribe una función que acepta cualquier cosa con el método run(), sin verificar. En el código aparece un objeto sin este método, y el error ocurre solo en tiempo de ejecución.
Ventajas:
Desventajas:
Uso de duck typing, pero con try/except y documentación de la interfaz:
def run_task(obj): try: obj.run() except AttributeError: print("¡El objeto no soporta la ejecución de la tarea!")
Ventajas:
Desventajas: