Última actualización: 04 de diciembre de 2023

9.4. Herencia

La herencia es una de las premisas y técnicas de la POO la cual permite a los programadores crear una clase general primero y luego más tarde crear clases más especializadas que re-utilicen código de la clase general. La herencia también le permite escribir un código más limpio y legible.

9.4.1. Clase Base

Clase Base o también conocida como Clase abstracta le permite definir una clase que puede heredarse en otras clases los atributos y comportamientos definido en esta.

../_images/poo_clase_abstracta.png

Figura 9.3, Figura 9.3, Clase base o abstracta

Use el diagrama anterior para ilustrar el concepto de la herencia, vea el caso de dos clases que tiene algo en común, ambas son personas, con atributos de datos personales y comportamiento típicos como hablar, comer, caminar, entonces para eso se crea una clase base llamada Persona. A continuación un ejemplo de la clase Persona con un método interno:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
class Persona:
    """Clase que representa una Persona"""

    def __init__(self, cedula, nombre, apellido, sexo):
        """Constructor de clase Persona"""
        self.cedula = cedula
        self.nombre = nombre
        self.apellido = apellido
        self.sexo = sexo

    def __str__(self):
        """Devuelve una cadena representativa de Persona"""
        return "{}: {}, {} {}, {}.".format(
            self.__doc__[25:34],
            str(self.cedula),
            self.nombre,
            self.apellido,
            self.obtener_genero(self.sexo),
        )

    def hablar(self, mensaje):
        """Mostrar mensaje de saludo de Persona"""
        return mensaje

    def obtener_genero(self, sexo):
        """Mostrar el genero de la Persona"""
        genero = ("Masculino", "Femenino")
        if sexo == "M":
            return genero[0]
        elif sexo == "F":
            return genero[1]
        else:
            return "Desconocido"

En el ejemplo previo, es donde empieza a crear una clase (lo hace con la palabra class). La segunda palabra Persona es el nombre de la clase. La tercera palabra que se encuentra dentro de los paréntesis este hace referencia al objeto object, usando para indicar la clase de la cual precede.

La clase Persona tiene los métodos __init__, __str__, hablar y obtener_genero. Sus atributos son cedula, nombre, apellido y sexo.

La instancia de dos nuevos objetos Persona seria de la siguiente forma:

1
2
persona1 = Persona("V-26938401", "Leonardo", "Caballero", "M")
persona2 = Persona("V-23569874", "Ana", "Poleo", "F")

El método constructor __init__ es un método especial el cual debe escribir como: MiClase(parámetros iniciales si hay cualquiera).

Usted puede llamar esos métodos y atributos con la siguiente notación: claseinstancia.metodo o claseinstancia.atributo.

>>> print(persona1.nombre, persona1.apellido)
>>> print(persona1.obtener_genero(persona1.sexo))

El método __str__ es un método usando para imprimir la descripción de la instancia de objeto el cual debe mostrar como:

1
print("\n" + str(persona1) + "\n")

En el anterior código se usan para cierto formato para imprimir la instancia de objeto usando la sentencia print, concatenando el carácter \n para generar un salto de página y seguidamente convertir a formato cadena de caracteres usando la función str() a la instancia de objeto llamada persona2.

9.4.2. Herencia simple

La herencia simple se apoya en el uso de clases base para compartir sus atributos y comportamientos con otros clases derivadas como los siguiente ejemplos el objeto Supervisor y el objeto Obrero.

../_images/poo_herencia.png

Figura 9.4, Figura 9.4, Diagrama de herencia de Objetos

El siguiente es un ejemplo de la clase Supervisor que derivada de la clase Persona con función interna:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
class Supervisor(Persona):
    """Clase que representa a un Supervisor"""

    def __init__(self, cedula, nombre, apellido, sexo, rol):
        """Constructor de clase Supervisor"""

        # Invoca al constructor de clase Persona
        Persona.__init__(self, cedula, nombre, apellido, sexo)

        # Nuevos atributos
        self.rol = rol
        self.tareas = ["10", "11", "12", "13"]

    def __str__(self):
        """Devuelve una cadena representativa al Supervisor"""
        return "{}: {} {}, rol: '{}', sus tareas: {}.".format(
            self.__doc__[26:37],
            self.nombre,
            self.apellido,
            self.rol,
            self.consulta_tareas(),
        )

    def consulta_tareas(self):
        """Mostrar las tareas del Supervisor"""
        return ", ".join(self.tareas)

Ahora, se creará una nueva clase Supervisor con los mismos métodos y atributos como la clase Persona, pero con dos nuevos atributos rol y tareas. No se copia la clase previa, pero si se hereda de ella.

La instancia del nuevo objeto Supervisor seria de la siguiente forma:

1
supervisor1 = Supervisor("V-16987456", "Jen", "Paz", "D", "Chivo")

Luego que genera la instancia del nuevo objeto Supervisor llamada supervisor1 se puede imprimir sus detalles de la siguiente forma:

1
print("\n" + str(supervisor1) + "\n")

Como la instancia de objeto supervisor1 hereda los atributo(s) y método(s) de la clase Persona usted puede reusarlo y llamarlo de la siguiente forma:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
# Atributo(s) y Método(s) heredado de la clase Persona
print(f"- Cédula de identidad: {supervisor1.cedula}.")
print(f"- Nombre completo: {supervisor1.nombre} {supervisor1.apellido}.")
print(f"- Genero: {supervisor1.obtener_genero(supervisor1.sexo)}.")
print(
    "- {} {} dijo: {}".format(
        supervisor1.nombre,
        supervisor1.apellido,
        supervisor1.hablar("A trabajar Leonardo!!!".upper()),
    )
)

# Atributo(s) y Método(s) heredado de la clase Supervisor
print(f"- Rol: {supervisor1.rol}.")
print(f"- N. Tareas: {supervisor1.consulta_tareas()}.")

Si desea usar los atributo(s) y método(s) heredados de la clase Supervisor se puede imprimir de la siguiente forma:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
print(
    """\nHola, Soy el {} {} {}, mi cédula es '{}',
mi genero '{}', con el rol '{}' y mis tareas
asignadas '{}'.""".format(
        supervisor1.__doc__[26:37].lower(),
        supervisor1.nombre,
        supervisor1.apellido,
        supervisor1.cedula,
        supervisor1.obtener_genero(supervisor1.sexo),
        supervisor1.rol,
        supervisor1.consulta_tareas(),
    )
)

Nota

El uso de las clases y la programación orientada a objetos, le permite a usted que pueda organizar el código con diferentes clases correspondientes a diferentes objetos que encontrará (una clase Persona, una clase Carro, una clase Departamento, etc.), con sus propios métodos y atributos.

Luego puede usar la herencia para considerar las variaciones en torno a una clase base y reutilizar el código. Ej.: a partir de una clase base de Persona, usted puede crear clases derivadas como Supervisor, JefeCuadrilla, Obrero, etc.

9.4.2.1. Función issubclass()

issubclass(), es una función integrada la cual le permite corroborar si un objeto es instancia de una clase.

Nota

Más información consulte la documentación detallada de la función issubclass().

Importante

Usted puede descargar el código usado en esta sección haciendo clic en los siguientes enlaces: clases.py y herencia_simple.py.

Truco

Para ejecutar el código clases.py y herencia_simple.py, abra una consola de comando, acceda al directorio donde se encuentra ambos programas:

leccion9/
├── clases.py
└── herencia_simple.py

Si tiene la estructura de archivo previa, entonces ejecute el siguiente comando:

$ python herencia_simple.py

9.4.3. Herencia múltiple

A diferencia de lenguajes como Java y C#, el lenguaje Python permite la herencia múltiple, es decir, se puede heredar de múltiples clases.

La herencia múltiple es la capacidad de una subclase de heredar de múltiples súper clases.

Esto conlleva un problema, y es que si varias súper clases tienen los mismos atributos o métodos, la subclase sólo podrá heredar de una de ellas.

En estos casos Python dará prioridad a las clases más a la izquierda en el momento de la declaración de la subclase:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
class Supervisor(Persona):
    """Clase que representa a un Supervisor"""

    def __init__(self, cedula, nombre, apellido, sexo, rol):
        """Constructor de clase Supervisor"""

        # Invoca al constructor de clase Persona
        Persona.__init__(self, cedula, nombre, apellido, sexo)

        # Nuevos atributos
        self.rol = rol
        self.tareas = ["10", "11", "12", "13"]

    def __str__(self):
        """Devuelve una cadena representativa al Supervisor"""
        return "{}: {} {}, rol: '{}', sus tareas: {}.".format(
            self.__doc__[26:37],
            self.nombre,
            self.apellido,
            self.rol,
            self.consulta_tareas(),
        )

    def consulta_tareas(self):
        """Mostrar las tareas del Supervisor"""
        return ", ".join(self.tareas)


class Destreza:
    """Clase la cual representa la Destreza de la Persona"""

    def __init__(self, area, herramienta, experiencia):
        """Constructor de clase Destreza"""
        self.area = area
        self.herramienta = herramienta
        self.experiencia = experiencia

    def __str__(self):
        """Devuelve una cadena representativa de la Destreza"""
        return """Destreza en el área {} con la herramienta {},
        tiene {} años de experiencia.""".format(
            str(self.area),
            self.experiencia,
            self.herramienta,
        )


class JefeCuadrilla(Supervisor, Destreza):
    """Clase la cual representa al Jefe de Cuadrilla"""

    def __init__(
        self,
        cedula,
        nombre,
        apellido,
        sexo,
        rol,
        area,
        herramienta,
        experiencia,
        cuadrilla,
    ):
        """Constructor de clase Jefe de Cuadrilla"""

        # Invoca al constructor de clase Supervisor
        Supervisor.__init__(self, cedula, nombre, apellido, sexo, rol)
        # Invoca al constructor de clase Destreza
        Destreza.__init__(self, area, herramienta, experiencia)

        # Nuevos atributos
        self.cuadrilla = cuadrilla

    def __str__(self):
        """Devuelve cadena representativa al Jefe de Cuadrilla"""
        jq = "{0}: {1} {2}, rol '{3}', tareas {4}, cuadrilla: {5}"
        return jq.format(
            self.__doc__[28:46],
            self.nombre,
            self.apellido,
            self.rol,
            self.consulta_tareas(),
            self.cuadrilla,
        )

9.4.3.1. Method Resolution Order (MRO)

Ese es el orden en el cual el método debe heredar en la presencia de herencia múltiple. Usted puede ver el MRO usando el atributo __mro__.

>>> JefeCuadrilla.__mro__
(<class '__main__.JefeCuadrilla'>,
<class '__main__.Supervisor'>,
<class '__main__.Persona'>,
<class '__main__.Destreza'>,
<type 'object'>)

>>> Supervisor.__mro__
(<class '__main__.Supervisor'>,
<class '__main__.Persona'>,
<type 'object'>)

>>> Destreza.__mro__
(<class '__main__.Destreza'>,
<type 'object'>)

El MRO es calculado en Python de la siguiente forma:

Un método en la llamada derivada es siempre llamada antes de método de la clase base. En nuestro ejemplo, la clase JefeCuadrilla es llamada antes de las clases Supervisor o Destreza. Esas dos clases son llamada antes de la clase Persona y la clase Persona es llamada antes de la clase object.

Si hay herencia múltiple como JefeCuadrilla(Supervisor, Destreza), el método invoca a Supervisor primero por que ese aparece primero de izquierda a derecha.

Importante

Usted puede descargar el código usado en esta sección haciendo clic en los siguientes enlaces: clases.py y herencia_multiple.py.

Truco

Para ejecutar el código clases.py y herencia_multiple.py, abra una consola de comando, acceda al directorio donde se encuentra ambos programas:

leccion9/
├── clases.py
└── herencia_multiple.py

Si tiene la estructura de archivo previa, entonces ejecute el siguiente comando:

$ python herencia_multiple.py

Ver también

Consulte la sección de lecturas suplementarias del entrenamiento para ampliar su conocimiento en esta temática.


¿Cómo puedo ayudar?

¡Mi soporte está aquí para ayudar!

Mi horario de oficina es de lunes a sábado, de 9 AM a 5 PM. GMT-4 - Caracas, Venezuela.

La hora aquí es actualmente 7:35 PM GMT-4.

Mi objetivo es responder a todos los mensajes dentro de un día hábil.

Contrata mi increíble soporte profesional