Herencia de clases de datos en Python
- Herencia en Python
- Herencia multinivel en Python
- Mezcle atributos predeterminados y no predeterminados entre la clase base y la subclase mediante la herencia de clases de datos en Python
- Conclusión
Las versiones 3.7 y posteriores introdujeron la herencia clase de datos en Python. Este artículo explica ampliamente las herencias de varios niveles y cómo usar la herencia de clases de datos en Python.
Herencia en Python
La herencia de clases de datos en Python se usa para obtener datos en subclases de su clase principal, lo que ayuda a reducir la repetición de códigos y hace que el código sea reutilizable.
Veamos un ejemplo de herencia:
El programa importa el paquete de la biblioteca dataclass
para permitir la creación de clases decoradas. La primera clase que se crea aquí es Padre
, que tiene dos métodos de miembro: la cadena nombre
y el entero edad
.
Luego se crea aquí una subclase de Padre
. La clase Niño
introduce un nuevo método de miembro: escuela
.
Se crea un objeto de instancia jack
para la clase Parent
, que pasa dos argumentos a la clase. Otro objeto de instancia, jack_son
, está hecho para la clase Niño
.
Como la clase Niño
es una subclase de Padre
, los miembros de datos se pueden derivar en la clase Niño
. Esta es la característica principal de la herencia de clases de datos en Python.
from dataclasses import dataclass
@dataclass
class Parent:
name: str
age: int
def print_name(self):
print(f"Name is '{self.name}' and age is= {self.age}")
@dataclass
class Child(Parent):
school: str
jack = Parent("Jack snr", 35)
jack_son = Child("Jack jnr", 12, school="havard")
jack_son.print_name()
Producción :
C:\python38\python.exe "C:/Users/Win 10/PycharmProjects/class inheritance/2.py"
Name is 'Jack jnr' and age is= 12
Process finished with exit code 0
Herencia multinivel en Python
Como hemos visto cómo funciona la herencia de clases de datos en Python, ahora veremos el concepto de herencia multinivel. Es un tipo de herencia en el que una subclase creada a partir de una clase principal se usa como principal para la clase secundaria subsiguiente.
El siguiente ejemplo demuestra la herencia de varios niveles de forma sencilla:
Clase principal
Se crea una clase Padre
con un constructor __init__
y un método miembro: print_method
. El constructor imprime la declaración "Inicializado en padre"
para que se muestre cuando se invoca la clase desde su clase secundaria.
La función print_method
tiene un parámetro b
, que se imprime cuando se llama a este método.
Clase infantil
La clase Child
se deriva de Parent
e imprime una declaración dentro de su constructor. El super().__init__
se refiere a la clase base con la clase secundaria.
Usamos super()
para que cualquier herencia múltiple cooperativa potencial utilizada por las clases secundarias llame a la siguiente función de clase principal apropiada en el Orden de resolución de métodos (MRO).
El método miembro print_method
está sobrecargado y una declaración de impresión imprime el valor de b
. Aquí también, super()
se refiere al método miembro de su clase padre.
Clase de nieto
En este punto, el programa simplemente repite (repetir códigos) la estructura para crear otra clase heredada de la clase Niño
. Dentro de print_method
, el valor de b
se incrementa usando super()
.
Función principal
Por último, se crea la función main
, que crea un objeto ob
y se convierte en una instancia de GrandChild()
. Por último, el objeto ob
invoca el print_method
.
Así es como se apilan las clases de varios niveles cuando se usa la herencia de clases de datos en Python.
class Parent:
def __init__(self):
print("Initialized in Parent")
def print_method(self, b):
print("Printing from class Parent:", b)
class Child(Parent):
def __init__(self):
print("Initialized in Child")
super().__init__()
def print_method(self, b):
print("Printing from class Child:", b)
super().print_method(b + 1)
class GrandChild(Child):
def __init__(self):
print("Initialized in Grand Child")
super().__init__()
def print_method(self, b):
print("Printing from class Grand Child:", b)
super().print_method(b + 1)
if __name__ == "__main__":
ob = GrandChild()
ob.print_method(10)
Producción :
C:\python38\python.exe "C:/Users/Win 10/PycharmProjects/class inheritance/3.py"
Initialized in Grand Child
Initialized in Child
Initialized in Parent
Printing from class Grand Child: 10
Printing from class Child: 11
Printing from class Parent: 12
Process finished with exit code 0
Entendamos lo que hace el código aquí:
La función main
pasa el valor 10
a la función print_method
de la clase Grand Child
. Según el MRO (Orden de resolución de métodos), el programa primero ejecuta la clase Grand Child
, imprime la declaración __init__
y luego pasa a su padre, la clase Child
.
La clase Child
y la clase Parent
imprimen sus declaraciones __init__
según MRO, y luego el compilador vuelve al print_method
de la clase GrandChild
. Este método imprime 10
(el valor de b
) y luego usa super()
para incrementar el valor de b
en su superclase, la Clase Niño
.
Luego, el compilador pasa al método de impresión
de la clase Niño
e imprime 11
. Luego, en el último nivel de MRO está la clase Padre
que imprime 12
.
El programa sale porque no existe ninguna superclase sobre la clase Padre
.
Como hemos entendido cómo funciona la herencia multinivel en la herencia de clases de datos en Python, la siguiente sección cubrirá el concepto de heredar atributos de una clase principal y cómo modificarlos.
Mezcle atributos predeterminados y no predeterminados entre la clase base y la subclase mediante la herencia de clases de datos en Python
Hemos visto cómo usar una clase secundaria para acceder a los miembros de datos de su clase principal en la herencia de clases de datos en Python y cómo funciona la herencia de varios niveles. Ahora, surge una pregunta si una clase secundaria puede acceder a los datos de los miembros de su superclase, ¿puede hacerle cambios?
La respuesta es sí, pero debe evitar TypeErrors. Por ejemplo, en el siguiente programa, hay dos clases, una clase Padre
y una subclase Niño
.
La clase Parent
tiene tres miembros de datos: name
, age
y una variable bool ugly
que se establece como False
de forma predeterminada. Los métodos de tres miembros imprimen el nombre
, la edad
y la identificación.
Ahora, dentro de la clase decorada Niño
derivada de Padre
, se introduce un nuevo miembro de datos: escuela
. Con él, la clase cambia el atributo de la variable feo
de False
a True
.
Se crean dos objetos, jack
para Padre
y jack_son
para Niño
. Estos objetos pasan argumentos a sus clases, y ambos objetos invocan el método print_id
e imprimen los detalles.
Un problema importante con el uso de este método para cambiar los valores predeterminados de la clase base es que provoca un TypeError.
from dataclasses import dataclass
@dataclass
class Parent:
name: str
age: int
ugly: bool = False
def print_name(self):
print(self.name)
def print_age(self):
print(self.age)
def print_id(self):
print(f"ID: Name - {self.name}, age = {self.age}")
@dataclass
class Child(Parent):
school: str
ugly: bool = True
jack = Parent("jack snr", 32, ugly=True)
jack_son = Child("Mathew", 14, school="cambridge", ugly=True)
jack.print_id()
jack_son.print_id()
Producción :
raise TypeError(f'non-default argument {f.name!r} '
TypeError: non-default argument 'school' follows default argument
Una razón detrás de este error es que en la herencia de clases de datos en Python, los atributos no se pueden usar con los valores predeterminados en una clase base y luego se pueden usar sin un valor predeterminado (atributos posicionales) en una subclase debido a la forma en que las clases de datos mezclan los atributos.
Esto se debe a que los atributos se fusionan desde el principio en la parte inferior del MRO y crean una lista ordenada de atributos en el orden en que se ven por primera vez, y las anulaciones permanecen en sus posiciones originales.
Con feo
por defecto, el Padre
comienza con nombre
, edad
y feo
, y luego el Niño
añade escuela
al final de esa lista (con feo
ya en el lista).
Esto da como resultado tener nombre
, edad
, feo
y escuela
en la lista, y dado que escuela
no tiene un valor predeterminado, la función __init__
muestra el resultado como un parámetro incorrecto.
Cuando el decorador @dataclass
crea una nueva clase de datos, busca en todas las clases base de la clase en MRO inverso (comenzando en el objeto) y agrega los campos de cada clase base a un mapeo ordenado de campos para cada clase de datos. encuentra
Luego agrega sus campos a la asignación ordenada después de que se hayan agregado todos los campos de la clase base. Todos los métodos creados utilizarán esta asignación ordenada calculada combinada de campos.
Debido a la disposición de los campos, las clases derivadas reemplazan a las clases base.
Si un campo sin valor predeterminado sigue a uno con un valor predeterminado, se generará un TypeError. Esto es cierto ya sea que suceda en una sola clase o debido a la herencia de clases.
La primera alternativa para solucionar este problema es forzar los campos con valores predeterminados a una posición posterior en el orden MRO utilizando diferentes clases base. Evite establecer campos directamente en las clases que se utilizarán como clases base, como Padre
, a toda costa.
Este programa tiene clases base con campos, y los campos sin valores predeterminados están separados. Las clases públicas se derivan de las clases base-con
y base-sin
.
Las subclases de la clase pública ponen la clase base al frente.
from dataclasses import dataclass
@dataclass
class _ParentBase:
name: str
age: int
@dataclass
class _ParentDefaultsBase:
ugly: bool = False
@dataclass
class _ChildBase(_ParentBase):
school: str
@dataclass
class _ChildDefaultsBase(_ParentDefaultsBase):
ugly: bool = True
@dataclass
class Parent(_ParentDefaultsBase, _ParentBase):
def print_name(self):
print(self.name)
def print_age(self):
print(self.age)
def print_id(self):
print(f"ID: Name - {self.name}, age = {self.age}")
@dataclass
class Child(_ChildDefaultsBase, Parent, _ChildBase):
pass
Amit = Parent("Amit snr", 32, ugly=True)
Amit_son = Child("Amit jnr", 12, school="iit", ugly=True)
Amit.print_id()
Amit_son.print_id()
Producción :
C:\python38\python.exe "C:/main.py"
The Name is Amit snr and Amit snr is 32 year old
The Name is Amit jnr and Amit jnr is 12 year old
Process finished with exit code 0
El MRO creado aquí prioriza los campos sin valores predeterminados sobre los campos con valores predeterminados dividiendo los campos en clases base separadas con campos “sin valores predeterminados” y “con valores predeterminados” y eligiendo cuidadosamente el orden de herencia. El MRO del Niño
es:
<class 'object'>
||
<class '__main__._ParentBase'>,
||
<class '__main__._ChildBase'>
||
<class '__main__._ParentDefaultsBase'>,
||
<class '__main__.Parent'>,
||
<class '__main__._ChildDefaultsBase'>,
||
<class '__main__._Child'>
Aunque Parent
no crea ningún campo nuevo, hereda los campos de ParentDefaultsBase
y no debería ocupar el último lugar en el orden de la lista de campos. Por lo tanto, la ChildDefaultsBase
se mantiene finalmente para cumplir con el tipo de pedido correcto.
Las reglas de clase de datos también se cumplen ya que las clases ParentBase
y ChildBase
, que tienen campos sin valores predeterminados, vienen antes que ParentDefaultsBase
y ChildDefaultsBase
, que tienen campos con valores predeterminados.
Como resultado, Child
sigue siendo una subclase de Parent
, mientras que las clases Parent
y Child
tienen un orden de campo correcto:
# __ Program Above __
print(signature(Parent))
print(signature(Child))
Producción:
Conclusión
Este artículo explica en detalle la herencia de clases de datos en Python. Conceptos como clase de datos, clases secundarias, herencia multinivel y atributos de mezcla de la clase base a la subclase se explican detalladamente.
Artículo relacionado - Python Class
- Clase de generador de Python
- Fábrica de clases de Python
- Igualdad de clases de Python
- Propiedad abstracta de Python
- Serializar un objeto de clase de Python a JSON