Polimorfismo — un principio fundamental de la programación orientada a objetos que permite a los objetos con diferentes estructuras implementar la misma interfaz, lo que significa que pueden trabajar de manera intercambiable. En Python, con tipificación dinámica, el polimorfismo está organizado de manera bastante flexible.
En lenguajes con tipificación estática estricta (Java, C++), el soporte para el polimorfismo requiere la declaración de interfaces o clases abstractas. En Python, gracias al duck typing, el polimorfismo no está ligado a una jerarquía de herencia ni siquiera a una interfaz. Lo principal es que el objeto soporte los métodos/atributos necesarios.
Por un lado, esto hace que el lenguaje sea flexible y conveniente. Por otro lado, aumenta la probabilidad de errores en tiempo de ejecución debido a errores tipográficos o la ausencia del método necesario, que solo se manifestarán al ejecutar el código.
En Python se distinguen:
class Animal: def speak(self): raise NotImplementedError class Dog(Animal): def speak(self): return '¡Guau!' class Cat(Animal): def speak(self): return '¡Miau!' def animal_voice(animal): print(animal.speak()) animal_voice(Dog()) # ¡Guau! animal_voice(Cat()) # ¡Miau!
class Duck: def quack(self): return '¡Cuac!' def make_quack(animal): print(animal.quack()) make_quack(Duck()) # ¡Cuac!
Características clave:
¿Es la herencia la única manera de asegurar polimorfismo en Python?
No. Gracias al duck typing, cualquier función puede aceptar un objeto con los métodos necesarios, independientemente de su clase o jerarquía.
¿Se puede considerar polimorfismo la coincidencia de la firma solo por el nombre del método, y no por su esencia?
No. Si un objeto soporta el método necesario, pero su semántica difiere de la esperada, pueden ocurrir errores. El polimorfismo tiene éxito solo cuando hay coincidencia no solo de la interfaz, sino también del sentido.
¿Proporciona una clase abstracta protección contra errores de una interfaz mal implementada en las clases derivadas?
Solo parcialmente. Si un heredero no implementa el método abstracto, se producirá un TypeError al intentar crear una instancia. Pero si lo implementa de manera incorrecta, pueden ocurrir errores.
Se introduce una clase externa en la biblioteca de registro, que tiene un método log(), pero devuelve un objeto de datos en lugar de registrar en el diario. Los errores solo se manifiestan durante la explotación.
Ventajas:
Desventajas:
Las clases están provistas de pruebas, la interfaz se formaliza a través de una clase base abstracta y @abstractmethod, y la semántica de los métodos se documenta.
Ventajas:
Desventajas: