Verhalten der Vererbung

PythonPythonBeginner
Jetzt üben

This tutorial is from open-source community. Access the source code

💡 Dieser Artikel wurde von AI-Assistenten übersetzt. Um die englische Version anzuzeigen, können Sie hier klicken

Einführung

In diesem Lab werden Sie sich mit dem Verhalten von Vererbung in Python vertraut machen. Insbesondere werden Sie sich darauf konzentrieren, wie die super()-Funktion funktioniert und wie kooperative Vererbung implementiert wird. Vererbung ist ein grundlegendes Konzept in der objektorientierten Programmierung, das es Klassen ermöglicht, Attribute und Methoden von Elternklassen zu erben, um Code wiederzuverwenden und hierarchische Klassenstrukturen aufzubauen.

In dieser praktischen Übung werden Sie die verschiedenen Arten der Vererbung in Python verstehen, einschließlich einfacher und multipler Vererbung. Sie werden auch lernen, die super()-Funktion zu nutzen, um sich durch Vererbungsstrukturen zu bewegen, ein praktisches Beispiel für kooperative multiple Vererbung zu implementieren und diese Konzepte anzuwenden, um ein Validierungssystem zu entwickeln. Die Hauptdatei, die während dieses Labs erstellt wird, ist validate.py.


Skills Graph

%%%%{init: {'theme':'neutral'}}%%%% flowchart RL python(("Python")) -.-> python/ControlFlowGroup(["Control Flow"]) python(("Python")) -.-> python/ObjectOrientedProgrammingGroup(["Object-Oriented Programming"]) python/ControlFlowGroup -.-> python/conditional_statements("Conditional Statements") python/ObjectOrientedProgrammingGroup -.-> python/classes_objects("Classes and Objects") python/ObjectOrientedProgrammingGroup -.-> python/inheritance("Inheritance") python/ObjectOrientedProgrammingGroup -.-> python/encapsulation("Encapsulation") subgraph Lab Skills python/conditional_statements -.-> lab-132500{{"Verhalten der Vererbung"}} python/classes_objects -.-> lab-132500{{"Verhalten der Vererbung"}} python/inheritance -.-> lab-132500{{"Verhalten der Vererbung"}} python/encapsulation -.-> lab-132500{{"Verhalten der Vererbung"}} end

Einstieg in einfache und multiple Vererbung

In diesem Schritt werden wir uns mit den beiden Hauptarten der Vererbung in Python befassen: einfacher Vererbung und multipler Vererbung. Vererbung ist ein grundlegendes Konzept in der objektorientierten Programmierung, das es einer Klasse ermöglicht, Attribute und Methoden von anderen Klassen zu erben. Wir werden auch untersuchen, wie Python entscheidet, welche Methode aufgerufen wird, wenn es mehrere Kandidaten gibt, ein Prozess, der als Methodenauflösung (Method Resolution) bekannt ist.

Einfache Vererbung

Einfache Vererbung liegt vor, wenn Klassen eine einzelne Abstammungslinie bilden. Stellen Sie sich das wie ein Stammbaum vor, bei dem jede Klasse nur einen direkten Elternteil hat. Erstellen wir ein Beispiel, um zu verstehen, wie es funktioniert.

Öffnen Sie zunächst ein neues Terminal in der WebIDE. Sobald das Terminal geöffnet ist, starten Sie den Python-Interpreter, indem Sie den folgenden Befehl eingeben und dann die Eingabetaste drücken:

python3

Jetzt, da Sie sich im Python-Interpreter befinden, werden wir drei Klassen erstellen, die eine einfache Vererbungsreihe bilden. Geben Sie den folgenden Code ein:

class A:
    def spam(self):
        print('A.spam')

class B(A):
    def spam(self):
        print('B.spam')
        super().spam()

class C(B):
    def spam(self):
        print('C.spam')
        super().spam()

In diesem Code erbt Klasse B von Klasse A, und Klasse C erbt von Klasse B. Die super()-Funktion wird verwendet, um die Methode der Elternklasse aufzurufen.

Nachdem Sie diese Klassen definiert haben, können wir herausfinden, in welcher Reihenfolge Python nach Methoden sucht. Diese Reihenfolge wird Methodenauflösungsreihenfolge (Method Resolution Order, MRO) genannt. Um die MRO von Klasse C anzuzeigen, geben Sie den folgenden Code ein:

C.__mro__

Sie sollten eine Ausgabe ähnlich der folgenden sehen:

(<class '__main__.C'>, <class '__main__.B'>, <class '__main__.A'>, <class 'object'>)

Diese Ausgabe zeigt, dass Python zunächst in Klasse C nach einer Methode sucht, dann in Klasse B, dann in Klasse A und schließlich in der Basisklasse object.

Jetzt erstellen wir eine Instanz von Klasse C und rufen ihre spam()-Methode auf. Geben Sie den folgenden Code ein:

c = C()
c.spam()

Sie sollten die folgende Ausgabe sehen:

C.spam
B.spam
A.spam

Diese Ausgabe zeigt, wie super() in einer einfachen Vererbungsreihe funktioniert. Wenn C.spam() super().spam() aufruft, ruft es B.spam() auf. Dann, wenn B.spam() super().spam() aufruft, ruft es A.spam() auf.

Multiple Vererbung

Multiple Vererbung ermöglicht es einer Klasse, von mehr als einer Elternklasse zu erben. Dadurch hat eine Klasse Zugang zu den Attributen und Methoden aller ihrer Elternklassen. Sehen wir uns an, wie die Methodenauflösung in diesem Fall funktioniert.

Geben Sie den folgenden Code in Ihren Python-Interpreter ein:

class Base:
    def spam(self):
        print('Base.spam')

class X(Base):
    def spam(self):
        print('X.spam')
        super().spam()

class Y(Base):
    def spam(self):
        print('Y.spam')
        super().spam()

class Z(Base):
    def spam(self):
        print('Z.spam')
        super().spam()

Jetzt erstellen wir eine Klasse M, die von mehreren Elternklassen X, Y und Z erbt. Geben Sie den folgenden Code ein:

class M(X, Y, Z):
    pass

M.__mro__

Sie sollten die folgende Ausgabe sehen:

(<class '__main__.M'>, <class '__main__.X'>, <class '__main__.Y'>, <class '__main__.Z'>, <class '__main__.Base'>, <class 'object'>)

Diese Ausgabe zeigt die Methodenauflösungsreihenfolge für Klasse M. Python wird in dieser Reihenfolge nach Methoden suchen.

Erstellen wir eine Instanz von Klasse M und rufen ihre spam()-Methode auf:

m = M()
m.spam()

Sie sollten die folgende Ausgabe sehen:

X.spam
Y.spam
Z.spam
Base.spam

Beachten Sie, dass super() nicht einfach die Methode der unmittelbaren Elternklasse aufruft. Stattdessen folgt es der von der Kindklasse definierten Methodenauflösungsreihenfolge (MRO).

Erstellen wir eine weitere Klasse N mit den Elternklassen in einer anderen Reihenfolge:

class N(Z, Y, X):
    pass

N.__mro__

Sie sollten die folgende Ausgabe sehen:

(<class '__main__.N'>, <class '__main__.Z'>, <class '__main__.Y'>, <class '__main__.X'>, <class '__main__.Base'>, <class 'object'>)

Jetzt erstellen wir eine Instanz von Klasse N und rufen ihre spam()-Methode auf:

n = N()
n.spam()

Sie sollten die folgende Ausgabe sehen:

Z.spam
Y.spam
X.spam
Base.spam

Dies zeigt ein wichtiges Konzept: In Python's multipler Vererbung bestimmt die Reihenfolge der Elternklassen in der Klassendefinition die Methodenauflösungsreihenfolge. Die super()-Funktion folgt dieser Reihenfolge, unabhängig von der Klasse, aus der sie aufgerufen wird.

Wenn Sie mit der Erkundung dieser Konzepte fertig sind, können Sie den Python-Interpreter beenden, indem Sie den folgenden Code eingeben:

exit()

Aufbau eines Validierungssystems mit Vererbung

In diesem Schritt werden wir ein praktisches Validierungssystem unter Verwendung von Vererbung aufbauen. Vererbung ist ein mächtiges Konzept in der Programmierung, das es Ihnen ermöglicht, neue Klassen auf der Grundlage bestehender zu erstellen. Auf diese Weise können Sie Code wiederverwenden und organisiertere und modularere Programme erstellen. Indem Sie dieses Validierungssystem aufbauen, werden Sie sehen, wie Vererbung eingesetzt werden kann, um wiederverwendbare Codekomponenten zu erstellen, die auf verschiedene Weise kombiniert werden können.

Erstellen der Basisklasse für Validatoren

Zunächst müssen wir eine Basisklasse für unsere Validatoren erstellen. Dazu erstellen wir eine neue Datei in der WebIDE. So können Sie es machen: Klicken Sie auf "File" > "New File", oder verwenden Sie die Tastenkombination. Sobald die neue Datei geöffnet ist, benennen Sie sie validate.py.

Fügen wir nun etwas Code in diese Datei ein, um eine Basisklasse Validator zu erstellen. Diese Klasse wird als Grundlage für alle anderen Validatoren dienen.

## validate.py
class Validator:
    @classmethod
    def check(cls, value):
        return value

In diesem Code haben wir eine Validator-Klasse mit einer check-Methode definiert. Die check-Methode nimmt einen Wert als Argument und gibt ihn unverändert zurück. Der @classmethod-Decorator wird verwendet, um diese Methode zu einer Klassenmethode zu machen. Das bedeutet, dass wir diese Methode direkt auf der Klasse aufrufen können, ohne eine Instanz der Klasse erstellen zu müssen.

Hinzufügen von Typ-Validatoren

Als Nächstes fügen wir einige Validatoren hinzu, die den Typ eines Werts überprüfen. Diese Validatoren werden von der Validator-Klasse erben, die wir gerade erstellt haben. Gehen Sie zurück zur validate.py-Datei und fügen Sie den folgenden Code ein:

class Typed(Validator):
    expected_type = object
    @classmethod
    def check(cls, value):
        if not isinstance(value, cls.expected_type):
            raise TypeError(f'Expected {cls.expected_type}')
        return super().check(value)

class Integer(Typed):
    expected_type = int

class Float(Typed):
    expected_type = float

class String(Typed):
    expected_type = str

Die Typed-Klasse ist eine Unterklasse von Validator. Sie hat ein expected_type-Attribut, das zunächst auf object gesetzt ist. Die check-Methode in der Typed-Klasse überprüft, ob der gegebene Wert vom erwarteten Typ ist. Wenn nicht, wird ein TypeError ausgelöst. Wenn der Typ korrekt ist, ruft sie die check-Methode der Elternklasse mit super().check(value) auf.

Die Integer-, Float- und String-Klassen erben von Typed und geben den genauen Typ an, den sie überprüfen sollen. Beispielsweise überprüft die Integer-Klasse, ob ein Wert eine Ganzzahl ist.

Testen der Typ-Validatoren

Jetzt, da wir unsere Typ-Validatoren erstellt haben, lassen Sie uns sie testen. Öffnen Sie ein neues Terminal und starten Sie den Python-Interpreter, indem Sie den folgenden Befehl ausführen:

python3

Sobald der Python-Interpreter läuft, können wir unsere Validatoren importieren und testen. Hier ist ein Code, um sie zu testen:

from validate import Integer, String

Integer.check(10)  ## Should return 10

try:
    Integer.check('10')  ## Should raise TypeError
except TypeError as e:
    print(f"Error: {e}")

String.check('10')  ## Should return '10'

Wenn Sie diesen Code ausführen, sollten Sie etwas wie Folgendes sehen:

10
Error: Expected <class 'int'>
'10'

Wir können diese Validatoren auch in einer Funktion verwenden. Versuchen wir das:

def add(x, y):
    Integer.check(x)
    Integer.check(y)
    return x + y

add(2, 2)  ## Should return 4

try:
    add('2', '3')  ## Should raise TypeError
except TypeError as e:
    print(f"Error: {e}")

Wenn Sie diesen Code ausführen, sollten Sie sehen:

4
Error: Expected <class 'int'>

Hinzufügen von Wert-Validatoren

Bisher haben wir Validatoren erstellt, die den Typ eines Werts überprüfen. Jetzt fügen wir einige Validatoren hinzu, die den Wert selbst anstatt den Typ überprüfen. Gehen Sie zurück zur validate.py-Datei und fügen Sie den folgenden Code ein:

class Positive(Validator):
    @classmethod
    def check(cls, value):
        if value < 0:
            raise ValueError('Expected >= 0')
        return super().check(value)

class NonEmpty(Validator):
    @classmethod
    def check(cls, value):
        if len(value) == 0:
            raise ValueError('Must be non-empty')
        return super().check(value)

Der Positive-Validator überprüft, ob ein Wert nicht negativ ist. Wenn der Wert kleiner als 0 ist, wird ein ValueError ausgelöst. Der NonEmpty-Validator überprüft, ob ein Wert eine Länge ungleich Null hat. Wenn die Länge 0 ist, wird ein ValueError ausgelöst.

Kombinieren von Validatoren mit multipler Vererbung

Jetzt werden wir unsere Validatoren unter Verwendung von multipler Vererbung kombinieren. Multiple Vererbung ermöglicht es einer Klasse, von mehr als einer Elternklasse zu erben. Gehen Sie zurück zur validate.py-Datei und fügen Sie den folgenden Code ein:

class PositiveInteger(Integer, Positive):
    pass

class PositiveFloat(Float, Positive):
    pass

class NonEmptyString(String, NonEmpty):
    pass

Diese neuen Klassen kombinieren Typüberprüfung und Werteüberprüfung. Beispielsweise überprüft die PositiveInteger-Klasse, dass ein Wert sowohl eine Ganzzahl als auch nicht negativ ist. Die Reihenfolge der Vererbung spielt hier eine Rolle. Die Validatoren werden in der in der Klassendefinition angegebenen Reihenfolge überprüft.

Testen der kombinierten Validatoren

Lassen Sie uns unsere kombinierten Validatoren testen. Im Python-Interpreter führen Sie den folgenden Code aus:

from validate import PositiveInteger, PositiveFloat, NonEmptyString

PositiveInteger.check(10)  ## Should return 10

try:
    PositiveInteger.check('10')  ## Should raise TypeError
except TypeError as e:
    print(f"Error: {e}")

try:
    PositiveInteger.check(-10)  ## Should raise ValueError
except ValueError as e:
    print(f"Error: {e}")

NonEmptyString.check('hello')  ## Should return 'hello'

try:
    NonEmptyString.check('')  ## Should raise ValueError
except ValueError as e:
    print(f"Error: {e}")

Wenn Sie diesen Code ausführen, sollten Sie sehen:

10
Error: Expected <class 'int'>
Error: Expected >= 0
'hello'
Error: Must be non-empty

Dies zeigt, wie wir Validatoren kombinieren können, um komplexere Validierungsregeln zu erstellen.

Wenn Sie mit dem Testen fertig sind, können Sie den Python-Interpreter beenden, indem Sie den folgenden Befehl ausführen:

exit()
✨ Lösung prüfen und üben

Anwenden von Validatoren auf eine Aktienklasse

In diesem Schritt werden wir sehen, wie unsere Validatoren in einer realen Situation funktionieren. Validatoren sind wie kleine Prüfer, die sicherstellen, dass die Daten, die wir verwenden, bestimmten Regeln entsprechen. Wir werden eine Stock-Klasse erstellen. Eine Klasse ist wie ein Bauplan für die Erstellung von Objekten. In diesem Fall wird die Stock-Klasse eine Aktie an der Börse repräsentieren, und wir werden unsere Validatoren verwenden, um sicherzustellen, dass die Werte ihrer Attribute (wie die Anzahl der Anteile und der Preis) gültig sind.

Erstellen der Aktienklasse

Zunächst müssen wir eine neue Datei erstellen. In der WebIDE erstellen Sie eine neue Datei namens stock.py. Diese Datei wird den Code für unsere Stock-Klasse enthalten. Fügen Sie nun den folgenden Code in die stock.py-Datei ein:

## stock.py
from validate import PositiveInteger, PositiveFloat

class Stock:
    def __init__(self, name, shares, price):
        self.name = name
        self.shares = shares
        self.price = price

    @property
    def shares(self):
        return self._shares

    @shares.setter
    def shares(self, value):
        self._shares = PositiveInteger.check(value)

    @property
    def price(self):
        return self._price

    @price.setter
    def price(self, value):
        self._price = PositiveFloat.check(value)

    def cost(self):
        return self.shares * self.price

Lassen Sie uns analysieren, was dieser Code tut:

  1. Wir beginnen damit, die PositiveInteger- und PositiveFloat-Validatoren aus unserem validate-Modul zu importieren. Diese Validatoren helfen uns, sicherzustellen, dass die Anzahl der Anteile eine positive Ganzzahl und der Preis eine positive Fließkommazahl ist.
  2. Dann definieren wir eine Stock-Klasse. Innerhalb der Klasse haben wir eine __init__-Methode. Diese Methode wird aufgerufen, wenn wir ein neues Stock-Objekt erstellen. Sie nimmt drei Parameter entgegen: name, shares und price und weist sie den Attributen des Objekts zu.
  3. Wir verwenden Properties und Setter, um die Werte von shares und price zu validieren. Ein Property ist eine Möglichkeit, den Zugriff auf ein Attribut zu steuern, und ein Setter ist eine Methode, die aufgerufen wird, wenn wir versuchen, den Wert dieses Attributs zu setzen. Wenn wir das shares-Attribut setzen, wird die PositiveInteger.check-Methode aufgerufen, um sicherzustellen, dass der Wert eine positive Ganzzahl ist. Ebenso wird, wenn wir das price-Attribut setzen, die PositiveFloat.check-Methode aufgerufen, um sicherzustellen, dass der Wert eine positive Fließkommazahl ist.
  4. Schließlich haben wir eine cost-Methode. Diese Methode berechnet die Gesamtkosten der Aktie, indem sie die Anzahl der Anteile mit dem Preis multipliziert.

Testen der Aktienklasse

Jetzt, da wir unsere Stock-Klasse erstellt haben, müssen wir sie testen, um zu sehen, ob die Validatoren korrekt funktionieren. Öffnen Sie ein neues Terminal und starten Sie den Python-Interpreter. Sie können dies tun, indem Sie den folgenden Befehl ausführen:

python3

Sobald der Python-Interpreter läuft, können wir unsere Stock-Klasse importieren und testen. Geben Sie den folgenden Code in den Python-Interpreter ein:

from stock import Stock

## Create a valid stock
s = Stock('GOOG', 100, 490.10)
print(f"Name: {s.name}, Shares: {s.shares}, Price: {s.price}")
print(f"Cost: {s.cost()}")

## Try setting an invalid shares value
try:
    s.shares = -10
except ValueError as e:
    print(f"Error setting shares: {e}")

## Try setting an invalid price value
try:
    s.price = "not a price"
except TypeError as e:
    print(f"Error setting price: {e}")

Wenn Sie diesen Code ausführen, sollten Sie eine Ausgabe ähnlich der folgenden sehen:

Name: GOOG, Shares: 100, Price: 490.1
Cost: 49010.0
Error setting shares: Expected >= 0
Error setting price: Expected <class 'float'>

Diese Ausgabe zeigt, dass unsere Validatoren wie erwartet funktionieren. Die Stock-Klasse lässt uns keine ungültigen Werte für shares und price setzen. Wenn wir versuchen, einen ungültigen Wert zu setzen, wird ein Fehler ausgelöst, und wir können diesen Fehler abfangen und ausgeben.

Verständnis, wie Vererbung hilft

Einer der großen Vorteile der Verwendung unserer Validatoren ist, dass wir verschiedene Validierungsregeln leicht kombinieren können. Vererbung ist ein mächtiges Konzept in Python, das es uns ermöglicht, neue Klassen auf der Grundlage bestehender zu erstellen. Mit multipler Vererbung können wir die super()-Funktion verwenden, um Methoden aus mehreren Elternklassen aufzurufen.

Beispielsweise, wenn wir sicherstellen möchten, dass der Name der Aktie nicht leer ist, können wir die folgenden Schritte ausführen:

  1. Importieren Sie den NonEmptyString-Validator aus dem validate-Modul. Dieser Validator hilft uns, zu überprüfen, ob der Name der Aktie kein leerer String ist.
  2. Fügen Sie einen Property-Setter für das name-Attribut in der Stock-Klasse hinzu. Dieser Setter wird die NonEmptyString.check()-Methode verwenden, um den Namen der Aktie zu validieren.

Dies zeigt, wie Vererbung, insbesondere multiple Vererbung mit der super()-Funktion, es uns ermöglicht, Komponenten zu erstellen, die flexibel sind und in verschiedenen Kombinationen wiederverwendet werden können.

Wenn Sie mit dem Testen fertig sind, können Sie den Python-Interpreter beenden, indem Sie den folgenden Befehl ausführen:

exit()
✨ Lösung prüfen und üben

Zusammenfassung

In diesem Lab haben Sie die Funktionsweise der Vererbung in Python kennengelernt und mehrere Schlüsselkonzepte erfasst. Sie haben den Unterschied zwischen einfacher und multipler Vererbung untersucht, verstanden, wie die super()-Funktion die Methodenauflösungsreihenfolge (Method Resolution Order, MRO) durchläuft, gelernt, kooperative multiple Vererbung zu implementieren und die Vererbung eingesetzt, um ein praktisches Validierungssystem aufzubauen.

Sie haben auch einen flexiblen Validierungsrahmen unter Verwendung von Vererbung erstellt und ihn in einem realen Beispiel mit der Stock-Klasse angewendet. Dies zeigt, wie die Vererbung wiederverwendbare und kombinierbare Komponenten schaffen kann. Die wichtigsten Erkenntnisse umfassen die Funktionsweise von super() bei einfacher und multipler Vererbung, die Fähigkeit der multiplen Vererbung, Funktionalitäten zu kombinieren, und die Verwendung von Property-Settern mit Validatoren. Diese Konzepte sind grundlegend für die objektorientierte Programmierung in Python und werden in der Praxis häufig eingesetzt.