Datenklassenvererbung in Python

Jay Shaw 15 Februar 2024
  1. Vererbung in Python
  2. Vererbung auf mehreren Ebenen in Python
  3. Mischen Sie Standard- und Nicht-Standardattribute zwischen Basisklasse und Unterklasse mithilfe der Datenklassenvererbung in Python
  4. Abschluss
Datenklassenvererbung in Python

In den Versionen 3.7 und höher wurde die Datenklassen-Vererbung in Python eingeführt. In diesem Artikel werden mehrstufige Vererbungen und die Verwendung der Datenklassenvererbung in Python ausführlich erläutert.

Vererbung in Python

Die Vererbung von Datenklassen in Python wird verwendet, um Daten in Unterklassen von ihrer übergeordneten Klasse abzurufen, was dazu beiträgt, sich wiederholende Codes zu reduzieren und Code wiederverwendbar zu machen.

Schauen wir uns ein Beispiel für Vererbung an:

Das Programm importiert das Bibliothekspaket dataclass, um die Erstellung dekorierter Klassen zu ermöglichen. Die erste hier erstellte Klasse ist Parent, die zwei Member-Methoden hat - String name und Integer Alter.

Dann wird hier eine Unterklasse von Parent erstellt. Die Klasse Kind führt eine neue Mitgliedsmethode ein – Schule.

Für die Klasse Parent wird ein Instanzobjekt jack erzeugt, das zwei Argumente an die Klasse übergibt. Ein weiteres Instanzobjekt, jack_son, wird für die Klasse Child erstellt.

Da die Klasse Child eine Unterklasse von Parent ist, können die Datenelemente in der Klasse Child abgeleitet werden. Dies ist das Hauptmerkmal der Datenklassenvererbung in 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()

Ausgang:

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

Vererbung auf mehreren Ebenen in Python

Da wir gesehen haben, wie die Vererbung von Datenklassen in Python funktioniert, werden wir uns nun das Konzept der mehrstufigen Vererbung ansehen. Es ist eine Art der Vererbung, bei der eine aus einer übergeordneten Klasse erstellte Unterklasse als übergeordnete Klasse für die nachfolgende Enkelklasse verwendet wird.

Das folgende Beispiel demonstriert die mehrstufige Vererbung in einfacher Form:

Elternklasse

Eine Klasse Parent wird mit einem Konstruktor __init__ und einer Member-Methode – print_method – erstellt. Der Konstruktor gibt die Anweisung "Initialized in Parent" aus, damit sie angezeigt wird, wenn die Klasse von ihrer untergeordneten Klasse aufgerufen wird.

Die Funktion print_method hat einen Parameter b, der gedruckt wird, wenn diese Methode aufgerufen wird.

Kinderklasse

Die Klasse Child ist von Parent abgeleitet und gibt eine Anweisung in ihrem Konstruktor aus. Die super().__init__ bezieht sich auf die Basisklasse mit der Kindklasse.

Wir verwenden super(), damit alle potenziellen kooperativen Mehrfachvererbungen, die von untergeordneten Klassen verwendet werden, die entsprechende nächste Funktion der übergeordneten Klasse in der Method Resolution Order (MRO) aufrufen.

Die Member-Methode print_method ist überladen, und eine print-Anweisung gibt den Wert von b aus. Auch hier bezieht sich super() auf die Member-Methode ihrer Elternklasse.

Enkelklasse

An diesem Punkt erstellt das Programm nur Boilerplates (Wiederholungscodes) der Struktur, um eine weitere geerbte Klasse aus der Klasse Child zu erstellen. Innerhalb von print_method wird der Wert von b mit super() erhöht.

Hauptfunktion

Zuletzt wird die Funktion main erstellt, die ein Objekt ob erstellt und zu einer Instanz von GrandChild() gemacht wird. Zuletzt ruft das Objekt ob die print_method auf.

So werden mehrstufige Klassen gestapelt, wenn die Datenklassenvererbung in Python verwendet wird.

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)

Ausgang:

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

Lassen Sie uns verstehen, was der Code hier tut:

Die Funktion main übergibt den Wert 10 an die Funktion print_method der Klasse Grand Child. Gemäß der MRO (Method Resolution Order) führt das Programm zuerst die Klasse Grand Child aus, gibt die __init__-Anweisung aus und geht dann zu ihrer Elternklasse über – der Child-Klasse.

Die Klassen Child und Parent geben ihre __init__-Anweisungen gemäß MRO aus, und dann folgt der Compiler zurück zur print_method der Klasse GrandChild. Diese Methode gibt 10 (den Wert von b) aus und verwendet dann super(), um den Wert von b in ihrer Oberklasse, der Child-Klasse, zu erhöhen.

Dann geht der Compiler weiter zur print_method der Child-Klasse und gibt 11 aus. Dann gibt es in der letzten Ebene von MRO die Klasse Parent, die 12 druckt.

Das Programm wird beendet, da keine Oberklasse über der Klasse Eltern existiert.

Da wir verstanden haben, wie die Vererbung auf mehreren Ebenen bei der Vererbung von Datenklassen in Python funktioniert, behandelt der nächste Abschnitt das Konzept der Vererbung von Attributen von einer übergeordneten Klasse und wie man es ändert.

Mischen Sie Standard- und Nicht-Standardattribute zwischen Basisklasse und Unterklasse mithilfe der Datenklassenvererbung in Python

Wir haben gesehen, wie eine untergeordnete Klasse für den Zugriff auf Datenelemente ihrer übergeordneten Klasse bei der Datenklassenvererbung in Python verwendet wird und wie die Vererbung auf mehreren Ebenen funktioniert. Nun stellt sich die Frage, ob eine untergeordnete Klasse auf Datenelemente ihrer Oberklasse zugreifen kann, kann sie Änderungen daran vornehmen?

Die Antwort ist ja, aber TypeErrors müssen vermieden werden. Zum Beispiel gibt es im untenstehenden Programm zwei Klassen, eine Eltern-Klasse und eine Unterklasse Kind.

Die Klasse Eltern hat drei Datenelemente – name, Alter und eine Bool-Variable ugly, die standardmäßig auf False gesetzt ist. Drei Member-Methoden geben name, Alter und ID aus.

Jetzt wird innerhalb der dekorierten Klasse Child, die von Parent abgeleitet ist, ein neues Datenelement eingeführt – school. Damit ändert die Klasse das Attribut der Variable ugly von False auf True.

Zwei Objekte, jack für Parent und jack_son für Child, werden erstellt. Diese Objekte übergeben Argumente an ihre Klassen, und beide Objekte rufen die Methode print_id auf und geben die Details aus.

Ein Hauptproblem bei der Verwendung dieser Methode zum Ändern von Standardwerten der Basisklasse besteht darin, dass sie einen TypeError verursacht.

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()

Ausgang:

    raise TypeError(f'non-default argument {f.name!r} '
TypeError: non-default argument 'school' follows default argument

Ein Grund für diesen Fehler ist, dass bei der Datenklassenvererbung in Python die Attribute aufgrund der Art und Weise, wie Datenklassen Attribute mischen, nicht mit Standardwerten in einer Basisklasse und dann ohne Standardwert (Positionsattribute) in einer Unterklasse verwendet werden können.

Dies liegt daran, dass die Attribute von Anfang an unten im MRO zusammengeführt werden und eine geordnete Liste von Attributen in der Reihenfolge aufbauen, in der sie zuerst gesehen werden, wobei Überschreibungen an ihren ursprünglichen Positionen verbleiben.

Mit hässlich als Standard beginnt der Elternteil mit name, Alter und hässlich, und dann fügt das Kind am Ende dieser Liste Schule hinzu (wobei hässlich bereits in der Liste).

Dies führt dazu, dass name, Alter, hässlich und Schule in der Liste stehen, und da Schule keinen Standardwert hat, listet die __init__-Funktion das Ergebnis als falschen Parameter auf.

Wenn der Decorator @dataclass eine neue Datenklasse erstellt, durchsucht er alle Basisklassen der Klasse in umgekehrter MRO (beginnend beim Objekt) und fügt die Felder aus jeder Basisklasse zu einer geordneten Zuordnung von Feldern für jede Datenklasse hinzu findet.

Es fügt dann seine Felder der geordneten Zuordnung hinzu, nachdem alle Basisklassenfelder hinzugefügt wurden. Diese kombinierte berechnete geordnete Zuordnung von Feldern wird von allen erstellten Methoden verwendet.

Aufgrund der Anordnung der Felder ersetzen abgeleitete Klassen Basisklassen.

Wenn ein Feld ohne Standardwert auf ein Feld mit Standardwert folgt, wird ein TypeError generiert. Dies gilt unabhängig davon, ob dies in einer einzelnen Klasse oder aufgrund von Klassenvererbung geschieht.

Die erste Alternative, um dieses Problem zu umgehen, besteht darin, die Felder mit Standardwerten an eine spätere Position im MRO-Auftrag zu zwingen, indem andere Basisklassen verwendet werden. Vermeiden Sie auf jeden Fall, Felder direkt auf Klassen zu setzen, die als Basisklassen verwendet werden, wie z. B. Parent.

Dieses Programm hat Basisklassen mit Feldern und Felder ohne Standardwerte werden getrennt. Öffentliche Klassen leiten sich von den Klassen Basis mit und Basis ohne ab.

Die Unterklassen der öffentlichen Klasse stellen die Basisklasse in den Vordergrund.

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()

Ausgang:

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

Die hier erstellte MRO priorisiert Felder ohne Standardwerte gegenüber Feldern mit Standardwerten, indem sie die Felder in separate Basisklassen mit Feldern ohne Standardwerte und mit Standardwerten aufteilt und die Vererbungsreihenfolge sorgfältig auswählt. Die MRO des Kindes ist:

<class 'object'>
        ||
<class '__main__._ParentBase'>,
        ||
<class '__main__._ChildBase'>
        ||
<class '__main__._ParentDefaultsBase'>,
        ||
<class '__main__.Parent'>,
        ||
<class '__main__._ChildDefaultsBase'>,
        ||
<class '__main__._Child'>

Obwohl Parent keine neuen Felder erstellt, erbt es die Felder von ParentDefaultsBase und sollte in der Reihenfolge der Feldauflistung nicht an letzter Stelle stehen. So wird die ChildDefaultsBase zuletzt beibehalten, um den richtigen Auftragstyp zu erfüllen.

Auch die Datenklassenregeln werden erfüllt, da die Klassen ParentBase und ChildBase, die Felder ohne Vorgabe haben, vor ParentDefaultsBase und ChildDefaultsBase stehen, die Felder mit Vorgabe haben.

Infolgedessen ist Child immer noch eine Unterklasse von Parent, während die Klassen Parent und Child eine korrekte Feldreihenfolge haben:

# __ Program Above __

print(signature(Parent))
print(signature(Child))

Ausgang:

Signaturfunktion

Abschluss

Dieser Artikel erklärt die Vererbung von Datenklassen in Python im Detail. Konzepte wie Datenklasse, untergeordnete Klassen, Vererbung auf mehreren Ebenen und das Mischen von Attributen von Basisklasse zu Unterklasse werden ausführlich erklärt.

Verwandter Artikel - Python Class

Verwandter Artikel - Python Dataclass