Einführung
In diesem Lab erwerben Sie ein praktisches Verständnis der wichtigsten objektorientierten Programmierkonzepte (OOP) in Python. Wir beginnen mit der Kapselung (Encapsulation), lernen, wie Daten und Methoden innerhalb einer Klasse gebündelt und der Zugriff auf Daten mithilfe privater Attribute gesteuert werden.
Anschließend implementieren Sie die Vererbung (Inheritance), um Beziehungen zwischen Klassen aufzubauen, was die Wiederverwendung von Code fördert. Wir werden auch die Polymorphie (Polymorphism) untersuchen, die es ermöglicht, Objekte verschiedener Klassen einheitlich zu behandeln. Schließlich verwenden Sie die super()-Methode, um Methoden aus einer Elternklasse effektiv aufzurufen, und üben die Mehrfachvererbung (Multiple Inheritance), um zu sehen, wie eine Klasse von mehreren Elternklassen erben kann.
Kapselung mit Basisklassen erkunden
In diesem Schritt untersuchen wir die Kapselung (Encapsulation), ein zentrales OOP-Prinzip. Kapselung beinhaltet das Bündeln von Daten (Attributen) und den Methoden, die auf diesen Daten operieren, in einer einzigen Einheit, der Klasse. Sie beschränkt außerdem den direkten Zugriff auf den internen Zustand eines Objekts, was hilft, versehentliche Datenänderungen zu verhindern.
In Python verwenden wir eine Namenskonvention, um anzuzeigen, dass ein Attribut „privat“ ist. Das Voranstellen eines einzelnen Unterstrichs (z. B. _name) signalisiert, dass das Attribut für den internen Gebrauch bestimmt ist. Obwohl dies nicht strikt erzwungen wird, ist es eine starke Konvention, die Entwickler respektieren.
Wir beginnen damit, zwei separate Klassen, Dog und Cat, zu erstellen, um zu sehen, wie sie strukturiert werden können.
Suchen Sie zuerst im Dateiexplorer auf der linken Seite der WebIDE nach der Datei animal_classes.py. Öffnen Sie diese und fügen Sie den folgenden Python-Code hinzu. Dieser Code definiert eine Dog-Klasse und eine Cat-Klasse, die jeweils ein privates _name-Attribut und Methoden zur Interaktion damit besitzen.
## File: animal_classes.py
class Dog:
def __init__(self, name):
## Ein einzelner Unterstrich als Präfix kennzeichnet ein "privates" Attribut.
self._name = name
## Öffentliche Methode, um den Wert des privaten Attributs abzurufen.
def get_name(self):
return self._name
## Öffentliche Methode, um den Wert des privaten Attributs festzulegen.
def set_name(self, value):
self._name = value
def say(self):
print(f"{self._name} says: Woof!")
class Cat:
def __init__(self, name):
self._name = name
def get_name(self):
return self._name
def set_name(self, value):
self._name = value
def say(self):
print(f"{self._name} says: Meow!")
## Dieser Block wird nur ausgeführt, wenn das Skript direkt gestartet wird.
if __name__ == "__main__":
## Erstellen einer Instanz der Dog-Klasse
my_dog = Dog("Buddy")
print(f"Initial dog name: {my_dog.get_name()}")
## Ändern des Hundenamens mithilfe der Setter-Methode
my_dog.set_name("Rocky")
print(f"New dog name: {my_dog.get_name()}")
my_dog.say()
print("-" * 20)
## Erstellen einer Instanz der Cat-Klasse
my_cat = Cat("Whiskers")
print(f"Cat name: {my_cat.get_name()}")
my_cat.say()
Nachdem Sie den Code hinzugefügt haben, speichern Sie die Datei.
Führen wir nun das Skript aus, um die Kapselung in Aktion zu sehen. Öffnen Sie das Terminal in der WebIDE und führen Sie den folgenden Befehl aus:
python animal_classes.py
Sie sehen die folgende Ausgabe, die zeigt, dass wir über unsere öffentlichen Methoden get_name und set_name mit dem privaten Attribut _name interagieren.
Initial dog name: Buddy
New dog name: Rocky
Rocky says: Woof!
--------------------
Cat name: Whiskers
Whiskers says: Meow!
<div class="pt-24 -mt-20 relative -z-10 anchor-item" id="vererbung-und-polymorphismus-implementieren"></div>
## Vererbung und Polymorphismus implementieren
Im vorherigen Schritt ist Ihnen möglicherweise aufgefallen, dass die Klassen `Dog` und `Cat` viel identischen Code aufweisen (`__init__`, `get_name`, `set_name`). Dies ist eine perfekte Gelegenheit, die **Vererbung** (Inheritance) zu nutzen. Vererbung ermöglicht es einer neuen Klasse (dem Kind oder der Unterklasse), Attribute und Methoden von einer bestehenden Klasse (dem Elternteil oder der Superklasse) zu erben, was die Wiederverwendung von Code fördert.
Wir führen auch die **Polymorphie** (Polymorphism) ein, was „viele Formen“ bedeutet. In OOP bezieht es sich auf die Fähigkeit verschiedener Klassen, auf denselben Methodenaufruf auf ihre eigene, einzigartige Weise zu reagieren.
Lassen Sie uns unseren Code refaktorieren. Wir erstellen eine Elternklasse `Animal`, um den gemeinsamen Code aufzunehmen, und lassen `Dog` und `Cat` von ihr erben. Die Methode `say`, die für jede Klasse unterschiedlich ist, wird die Polymorphie demonstrieren.
Öffnen Sie die Datei `animal_classes.py` und ersetzen Sie deren gesamten Inhalt durch den folgenden Code:
```python
## File: animal_classes.py
class Animal:
def __init__(self, name):
self._name = name
def get_name(self):
return self._name
def set_name(self, value):
self._name = value
def say(self):
print(f"{self._name} makes a generic animal sound.")
## Dog erbt von Animal
class Dog(Animal):
## Dies überschreibt die say()-Methode aus der Animal-Klasse
def say(self):
print(f"{self._name} says: Woof!")
## Cat erbt von Animal
class Cat(Animal):
## Dies überschreibt ebenfalls die say()-Methode
def say(self):
print(f"{self._name} says: Meow!")
def make_animal_speak(animal_instance):
animal_instance.say()
if __name__ == "__main__":
generic_animal = Animal("Creature")
my_dog = Dog("Buddy")
my_cat = Cat("Whiskers")
print("--- Calling say() on each object ---")
generic_animal.say()
my_dog.say()
my_cat.say()
print("\n--- Demonstrating Polymorphism ---")
make_animal_speak(generic_animal)
make_animal_speak(my_dog)
make_animal_speak(my_cat)
Speichern Sie die Datei. Beachten Sie, wie die Klassen Dog und Cat nun viel einfacher sind. Sie erben die Methoden __init__, get_name und set_name von Animal. Jede Klasse stellt ihre eigene Version der Methode say bereit, was ein Beispiel für das Methodenüberschreiben (Method Overriding) ist.
Führen Sie nun das aktualisierte Skript im Terminal aus:
python animal_classes.py
Die Ausgabe wird sein:
--- Calling say() on each object ---
Creature makes a generic animal sound.
Buddy says: Woof!
Whiskers says: Meow!
--- Demonstrating Polymorphism ---
Creature makes a generic animal sound.
Buddy says: Woof!
Whiskers says: Meow!
Die Funktion make_animal_speak akzeptiert jedes Objekt, das eine say-Methode besitzt. Obwohl wir ihr verschiedene Objekttypen (Animal, Dog, Cat) übergeben, funktioniert sie korrekt, da jedes Objekt weiß, wie es die say-Aktion auf seine eigene Weise ausführen muss. Das ist die Stärke der Polymorphie.
Die super()-Methode zur Erweiterung der Funktionalität nutzen
Wenn eine Kindklasse eine Methode ihrer Elternklasse überschreibt, muss sie diese Methode manchmal erweitern und nicht nur ersetzen. Die super()-Funktion bietet eine Möglichkeit, die Methode der Elternklasse innerhalb der Kindklasse aufzurufen.
Dies ist bei der __init__-Methode sehr üblich. Eine Kindklasse muss oft ihre eigenen Initialisierungsschritte zusätzlich zu der vom Elternteil durchgeführten Initialisierung ausführen.
Fügen wir unseren Klassen Dog und Cat eindeutige Attribute hinzu. Der Dog erhält ein age (Alter) und die Cat erhält eine color (Farbe). Wir verwenden super(), um sicherzustellen, dass die __init__-Methode der Elternklasse Animal weiterhin aufgerufen wird, um den _name festzulegen.
Ändern Sie die Datei animal_classes.py, indem Sie deren Inhalt durch den folgenden Code ersetzen:
## File: animal_classes.py
class Animal:
def __init__(self, name):
print(f"Animal __init__ called for {name}")
self._name = name
def get_name(self):
return self._name
def set_name(self, value):
self._name = value
def say(self):
print(f"{self._name} makes a generic animal sound.")
class Dog(Animal):
def __init__(self, name, age):
## Ruft die __init__-Methode der Elternklasse auf, um das 'name'-Attribut zu verarbeiten
super().__init__(name)
print("Dog __init__ called")
self.age = age
def say(self):
## Wir können super() auch verwenden, um die say()-Methode der Elternklasse aufzurufen
## super().say()
print(f"{self._name} says: Woof! I am {self.age} years old.")
class Cat(Animal):
def __init__(self, name, color):
## Ruft die __init__-Methode der Elternklasse auf
super().__init__(name)
print("Cat __init__ called")
self.color = color
def say(self):
print(f"{self._name} says: Meow! I have {self.color} fur.")
if __name__ == "__main__":
my_dog = Dog("Buddy", 5)
my_dog.say()
print("-" * 20)
my_cat = Cat("Whiskers", "black")
my_cat.say()
Speichern Sie die Datei. In dieser Version rufen Dog.__init__ und Cat.__init__ zuerst super().__init__(name) auf. Dadurch wird der Code in Animal.__init__ ausgeführt, der das Attribut _name setzt. Danach fahren sie mit ihren eigenen spezifischen Initialisierungen fort (self.age = age und self.color = color).
Führen Sie das Skript im Terminal aus:
python animal_classes.py
Die Ausgabe demonstriert die Kette der __init__-Aufrufe und die erweiterten say-Methoden:
Animal __init__ called for Buddy
Dog __init__ called
Buddy says: Woof! I am 5 years old.
--------------------
Animal __init__ called for Whiskers
Cat __init__ called
Whiskers says: Meow! I have black fur.
<div class="pt-24 -mt-20 relative -z-10 anchor-item" id="mehrfachvererbung-%C3%BCben"></div>
## Mehrfachvererbung üben
Python erlaubt es einer Klasse, von mehr als einer Elternklasse zu erben. Dies wird als **Mehrfachvererbung** (Multiple Inheritance) bezeichnet. Sie kann ein mächtiges Werkzeug sein, um Funktionalitäten aus verschiedenen Quellen zu vermischen, führt aber auch zu Komplexität, insbesondere bei der Entscheidung Pythons, welche Methode der Elternklassen verwendet werden soll, wenn diese denselben Namen haben.
Diese Suchreihenfolge wird als **Method Resolution Order (MRO)** bezeichnet. Python verwendet einen Algorithmus namens C3-Linearisierung, um eine konsistente und vorhersagbare MRO zu bestimmen.
Lassen Sie uns dies anhand eines neuen Beispiels untersuchen. Öffnen Sie die Datei `multiple_inheritance.py` im Datei-Explorer und fügen Sie den folgenden Code hinzu:
```python
## File: multiple_inheritance.py
class ParentA:
def speak(self):
print("Speaking from ParentA")
def common_method(self):
print("ParentA's common method")
class ParentB:
def speak(self):
print("Speaking from ParentB")
def common_method(self):
print("ParentB's common method")
## Child erbt von A, dann B
class Child_AB(ParentA, ParentB):
pass
## Child erbt von B, dann A
class Child_BA(ParentB, ParentA):
def common_method(self):
print("Child_BA's own common method")
if __name__ == "__main__":
child1 = Child_AB()
child2 = Child_BA()
print("--- Investigating Child_AB (ParentA, ParentB) ---")
child1.speak()
child1.common_method()
## Die .mro()-Methode zeigt die Method Resolution Order an
print("MRO for Child_AB:", [c.__name__ for c in Child_AB.mro()])
print("\n--- Investigating Child_BA (ParentB, ParentA) ---")
child2.speak()
child2.common_method()
print("MRO for Child_BA:", [c.__name__ for c in Child_BA.mro()])
Speichern Sie die Datei. Hier erbt Child_AB von ParentA und dann von ParentB. Child_BA erbt in umgekehrter Reihenfolge. Wenn eine Methode aufgerufen wird, sucht Python in der Reihenfolge, die durch die MRO festgelegt ist, danach.
Führen Sie das Skript im Terminal aus:
python multiple_inheritance.py
Sie sehen die folgende Ausgabe:
--- Investigating Child_AB (ParentA, ParentB) ---
Speaking from ParentA
ParentA's common method
MRO for Child_AB: ['Child_AB', 'ParentA', 'ParentB', 'object']
--- Investigating Child_BA (ParentB, ParentA) ---
Speaking from ParentB
Child_BA's own common method
MRO for Child_BA: ['Child_BA', 'ParentB', 'ParentA', 'object']
Aus der Ausgabe können Sie beobachten:
child1.speak()ruft die Methode vonParentAauf, weilParentAin der MRO vonChild_ABzuerst kommt.child2.speak()ruft die Methode vonParentBauf, weilParentBin der MRO vonChild_BAzuerst kommt.child2.common_method()ruft die Version auf, die direkt inChild_BAdefiniert ist, da Python diese dort zuerst findet, bevor es die Elternklassen überprüft.
Das Verständnis der MRO ist entscheidend, um das Verhalten in Szenarien mit Mehrfachvererbung vorherzusagen.
Zusammenfassung
In diesem Lab haben Sie praktische Erfahrungen mit vier grundlegenden Konzepten der objektorientierten Programmierung (OOP) in Python gesammelt.
Sie begannen mit der Kapselung (Encapsulation) und lernten, Klassendaten konventionsgemäß durch private Attribute zu schützen und öffentliche Methoden für den Zugriff bereitzustellen. Anschließend haben Sie Ihren Code umstrukturiert, um Vererbung (Inheritance) zu nutzen, indem Sie eine Elternklasse Animal erstellten, um die Code-Duplizierung in den Unterklassen Dog und Cat zu reduzieren.
Bei der Implementierung der Vererbung haben Sie Polymorphismus (Polymorphism) in Aktion gesehen, da Dog- und Cat-Objekte unterschiedlich auf denselben Aufruf der say()-Methode reagierten. Sie lernten, die super()-Methode zu verwenden, um Funktionalität von einer Elternklasse aufzurufen und zu erweitern, insbesondere innerhalb der __init__-Methode. Schließlich haben Sie die Mehrfachvererbung (Multiple Inheritance) und die Bedeutung der Method Resolution Order (MRO) für die Bestimmung aufgerufen wird, welche Elternmethode aufgerufen wird, untersucht.



