Regístrate para acceder a más de 15 cursos gratuitos de programación con un simulador

Despacho dinámico Python: Profundizando en las clases

En lecciones anteriores ya hablamos sobre el polimorfismo y cómo funciona en Python. Ahora que hemos pasado la mitad del curso, es buen momento para profundizar más: vamos a ver cómo la herencia se conecta con el polimorfismo y cómo esto le da más poder y flexibilidad a nuestro código.


¿Qué es el polimorfismo?

Hagamos un pequeño repaso. El polimorfismo nos permite usar objetos de diferentes clases como si fueran del mismo tipo, siempre que compartan ciertos métodos. Es decir, podemos tratar a distintos objetos de manera uniforme si tienen las mismas interfaces (métodos con el mismo nombre).

Esto es útil para escribir código más general, que funcione con muchos tipos de objetos.


Pero, ¿cómo entra la herencia en todo esto?

Es importante saber que el polimorfismo en Python NO depende de la herencia. Sin embargo, la herencia sí tiene un papel especial en cómo se resuelven los métodos cuando usamos polimorfismo.

Pensemos un momento: si tenemos una jerarquía de clases (una clase base y otras que heredan de ella), cuando Python necesita ejecutar un método, comienza a buscarlo desde la clase del objeto. Si no lo encuentra allí, sigue buscando en sus clases padre, subiendo por la cadena de herencia.


Ejemplo: Un despachador de métodos

Este código simula lo que Python hace por debajo al llamar a un método:

class Dispatcher:
    def call_method(self, obj, method_name, *args):
        # Obtenemos todos los métodos que tiene el objeto
        methods = [method for method in dir(obj) if callable(getattr(obj, method))]

        if method_name in methods:
            # Si existe el método, lo llamamos
            getattr(obj, method_name)(*args)
        elif method_name not in methods and '__call__' in methods:
            # Si no existe el método, pero el objeto es "llamable", lo invocamos
            obj.__call__(*args)
        else:
            raise Exception('No method error')

Este código busca el método por nombre (como get_name) en el objeto. Si no lo encuentra pero el objeto tiene un método especial call(), entonces lo usa como último recurso.

Ahora viene lo importante: esta búsqueda no se queda solo en la clase del objeto. Si hay herencia, Python sube por la jerarquía (es decir, busca en la clase madre, luego en la abuela, y así).

¿Cuál es el problema con call?

El método __call__() es costoso de usar. Si está definido muy arriba en la jerarquía de clases (por ejemplo, en una clase base de muchas otras), puede volver lento tu código, ya que Python tendrá que hacer más pasos para encontrarlo.

¿Qué diferencia hay entre enlace tardío y despacho dinámico?

Hay dos términos que suenan parecidos, pero que no significan lo mismo, y vale la pena aclararlos:

Concepto ¿Qué hace?
Enlace (binding) Asocia una variable con un valor o tipo
Enlace tardío (late binding) Se hace durante la ejecución del programa
Despacho (dispatch) Selecciona qué función o método ejecutar dependiendo del tipo del objeto
Despacho dinámico Esta selección también se hace en tiempo de ejecución

Ejemplo de enlace tardío

En Python, incluso podemos usar una función antes de definirla (en algunos contextos), gracias a que Python no revisa todo el código antes de ejecutarlo.

Un ejemplo más común de enlace tardío es el uso de self:

class Animal:
    def hacer_sonido(self):
        print("Sonido genérico")

class Perro(Animal):
    def hacer_sonido(self):
        print("Guau!")

def reproducir_sonido(animal):
    animal.hacer_sonido()

mi_perro = Perro()
reproducir_sonido(mi_perro)  # Aunque no sabemos el tipo exacto de "animal", Python lo resuelve en ejecución

El tipo exacto de mi_perro se conoce solamente en tiempo de ejecución, y ahí es cuando se ejecuta el hacer_sonido() correcto. Eso es enlace tardío y también despacho dinámico.


Resumen

  • El polimorfismo en Python no requiere herencia, pero trabajan bien juntos.
  • Herencia permite que Python siga buscando métodos en la jerarquía de clases si no los encuentra en la clase original.
  • __call__() es un método especial que se usa como último recurso si no se encuentra el método que buscamos.
  • El uso de __call__() puede afectar el rendimiento si está muy arriba en la herencia.
  • Enlace tardío significa que Python decide qué objeto o método usar en el momento en que se ejecuta el código.
  • Despacho dinámico es cuando Python decide qué versión del método llamar, dependiendo del tipo real del objeto.
  • Ambas características permiten que Python sea muy flexible, pero es clave comprender cómo funcionan para evitar errores y escribir mejor código.

Materiales adicionales

  1. ¿Qué es la vinculación temprana y tardía?

Para acceder completo a curso necesitas un plan básico

El plan básico te dará acceso completo a todos los cursos, ejercicios y lecciones de Códica, proyectos y acceso de por vida a la teoría de las lecciones completadas. La suscripción se puede cancelar en cualquier momento.

Obtener acceso
130
cursos
1000
ejercicios
2000+
horas de teoría
3200
test

Obtén acceso

Cursos de programación para principiantes y desarrolladores experimentados. Comienza tu aprendizaje de forma gratuita

  • 130 cursos, 2000+ horas de teoría
  • 1000 ejercicios prácticos en el navegador
  • 360 000 estudiantes
Al enviar el formulario, aceptas el «Política de privacidad» y los términos de la «Oferta», y también aceptas los «Términos y condiciones de uso»

Nuestros graduados trabajan en empresas como:

Bookmate
Health Samurai
Dualboot
ABBYY