Herencia de Clases en Python
La herencia es uno de los pilares fundamentales de la Programación Orientada a Objetos (POO), junto con la encapsulación y el polimorfismo. Su propósito principal es facilitar la reutilización de código y establecer una relación jerárquica entre clases, permitiendo que una clase (subclase o clase derivada) adquiera propiedades y comportamientos de otra clase (superclase o clase base).
Herencia Simple
La herencia simple se produce cuando una clase hereda de una única clase base. La sbuclase puede acceder a los atributos y métodos públicos de su superclase. Es posible extender o mdoificar el comportamiento heredado.
Sintaxis y Uso
Para indicar que una clase hereda de otra, se coloca el nombre de la superclase entre paréntesis después del nombre de la subclase.
class DispositivoElectronico:
def __init__(self, marca):
self.marca = marca
print(f"Inicializando un dispositivo de marca {self.marca}")
def encender(self):
return f"El {self.marca} se ha encendido."
def apagar(self):
return f"El {self.marca} se ha apagado."
class Smartphone(DispositivoElectronico):
def __init__(self, marca, modelo):
# Llamar al constructor de la clase base usando super()
super().__init__(marca)
self.modelo = modelo
print(f"Configurando smartphone {self.marca} {self.modelo}")
def encender(self):
# Sobreescribir el método 'encender' de la clase base
return f"El {self.marca} {self.modelo} inicia rápidamente con su SO."
def hacer_llamada(self, numero):
return f"Realizando llamada al {numero} desde el {self.modelo}."
def mostrar_info_encendido(self):
# Llamar al método 'encender' de la clase base explícitamente
mensaje_base = DispositivoElectronico.encender(self)
return f"{mensaje_base} y ahora se cargan las aplicaciones."
# Demostración de herencia
mi_telefono = Smartphone("TechX", "UltraPro")
print(mi_telefono.encender())
print(mi_telefono.hacer_llamada("555-1234"))
print(mi_telefono.apagar()) # Método heredado
print(mi_telefono.mostrar_info_encendido())
En el ejemplo anterior, Smartphone hereda de DispositivoElectronico. El método encender es sobrescrito en Smartphone, mientras que apagar es heredado directamente. El método mostrar_info_encendido muestra cómo se puede invocar la implementación de la superclsae desde la subclase usando el nombre de la superclase directamente.
Reglas de Selección de Métodos (Herencia Simple)
Cuando un objeto de una subclase invoca un método:
- Primero, busca el método en su propia clase. Si lo encuentra, lo ejecuta.
- Si no lo encuentra en su propia clase, busca en la clase base inmediata.
- Este proceso continúa hacia arriba en la jerarquía de herencia hasta que se encuentra el método o se llega a la clase base
objectsin encontrarlo.
Herencia Múltiple
Python se distingue de algunos lenguajes (como Java, que solo soporta herencia simple de implementación) al permitir que una clase herede de múltiples clases base. Esto significa que una clase puede adquirir atributos y métodos de varias superclases.
Orden de Resolución de Métodos (MRO)
En la herencia múltiple, el orden en que Python busca métodos en la jerarquía de clases se llama Orden de Resolución de Métodos (MRO). Python utiliza un algoritmo llamado C3 linearization para determinar el MRO, el cual garantiza un orden consistente y predecible.
class Volador:
def moverse(self):
return "Volando por el aire."
class Nadador:
def moverse(self):
return "Nadando en el agua."
class Caminador:
def moverse(self):
return "Caminando sobre tierra."
class Pato(Volador, Nadador, Caminador):
pass
class Pingüino(Nadador, Caminador):
# Los pingüinos no vuelan
pass
print("--- Comportamiento del Pato ---")
mi_pato = Pato()
# El MRO determinará qué método 'moverse' se llama primero
print(mi_pato.moverse())
print("\n--- Comportamiento del Pingüino ---")
mi_pinguino = Pingüino()
print(mi_pinguino.moverse())
# Ver el MRO explícitamente
print("\nOrden de Resolución de Métodos (MRO) para Pato:")
print(Pato.__mro__)
print("\nOrden de Resolución de Métodos (MRO) para Pingüino:")
print(Pingüino.__mro__)
Como se puede observar, el MRO define el orden de búsqueda. Para Pato(Volador, Nadador, Caminador), el método moverse de Volador se llama porque es la primera clase base listada que lo implementa. Para Pingüino(Nadador, Caminador), se invoca el de Nadador.
La Clase Base object
En Python 3, todas las clases, incluso aquellas que no especifican una clase base explícitamente, heredan implícitamente de la clase object. Esta clase proporciona un conjunto de métodos básicos que son comunes a todos los objetos de Python, como __init__, __str__, __eq__, etc.
class MiClase:
pass # Es equivalente a class MiClase(object):
una_instancia = MiClase()
print(f"Clases base de MiClase: {MiClase.__bases__}")
Inspección de Clases y Herencia
Python proporciona herramientas para inspeccionar la jerarquía de herencia y los tipos de objetos en tiempo de ejecución.
__bases__: Es un atributo de clase que devuelve una tupla de las clases base directas de una clase.isinstance(objeto, clase): Una función incorporada que verifica si un objeto es una instancia de una clase dada o de una subclase de esa clase.
class Animal:
pass
class Mamifero(Animal):
pass
class Perro(Mamifero):
pass
class Gato(Mamifero):
pass
un_perro = Perro()
un_gato = Gato()
print(f"Clases base de Perro: {Perro.__bases__}")
print(f"Clases base de Gato: {Gato.__bases__}")
print(f"Clases base de Mamifero: {Mamifero.__bases__}")
print(f"\n¿'un_perro' es una instancia de Perro? {isinstance(un_perro, Perro)}")
print(f"¿'un_perro' es una instancia de Mamifero? {isinstance(un_perro, Mamifero)}")
print(f"¿'un_perro' es una instancia de Animal? {isinstance(un_perro, Animal)}")
print(f"¿'un_perro' es una instancia de Gato? {isinstance(un_perro, Gato)}")
# Comparación con type()
print(f"\n¿El tipo de 'un_perro' es exactamente Perro? {type(un_perro) is Perro}")
print(f"¿El tipo de 'un_perro' es exactamente Mamifero? {type(un_perro) is Mamifero}")
La función isinstance() es más flexible que una simple comparación de tipos con type() is, ya que considera toda la jerarquía de herencia. Esto es crucial para un código polimórfico robusto.