8. Objektorientierung¶
8.1. Einführung¶
In diesem Kapitel lernen wir eines der wichtigsten Programmierparadigmen - die Objektorientierung. Bereits im ersten Kapitel wurde diese angesprochen.
- Das Problem
Zwei der wichtigsten Programmierparadigmen sind:
- Imperative Programmierung (z.B. prozedural)
- Objektorientierte Programmierung (OOP)
Die Abbildung Fahrzeuge veranschaulicht beide Möglichkeiten.
Frage: Suche ein blaues Auto ohne die anderen Fahrzeuge zu stören/berühren? Es ist klar:
- Im Falle einer Sortierung geht es leichter, schneller
- aber die Sortierung braucht vorab Zeit!
Das ist das Grundprinzip der OOP!
- Grundidee
- Eine Software wird in Grundstrukturen zerlegt welche übersichtlich sind (Beispiel: Ein Auto besteht aus Motor, Chassis, Räder).
- Diese Grundstrukturen nennt man Klassen (Beispiel: Rad, Motor, … oder wie oben Auto, LKW, Bus)
- Von so einer Klasse können beliebig viele Objekte instanziert werden. (Beispiel: Rad 1, Rad 2, Rad 3, … oder wie oben Auto 1, Auto 2, Auto 3)
- Objekte bestehen aus Daten/Attributen (z.B. Raddurchmesser, Automarke, …) und Funktionen/Methoden (Berechne Radumfag, Sag die Automarke, … ), welche diese modifizieren, verarbeiten oder zurückgegeben.
- Grundbestandteile der OOP
- Datenkapselung
- Vererbung
- Polymorphismus
Im folgenden werden diese Dinge im Detail erklärt.
- Vorteile OOP
- Modularität (Wartbarkeit, leichter Fehler finden)
- Verbergen von Informationen (man wird nicht „erschlagen“ beim Code lesen)
- Wiederverwendbarkeit von Code (nicht mehrmals gleiche Funktion implementiert)
- Leichte Erweiterbarkeit (andere Programmteile werden nicht beeinflusst)
- Nachteile OOP
- Schwieriger und aufwendiger zu erstellen (Design + Implementierung)
- eventuell langsamere Ausführung (je nach Implementierung)
8.1.1. Prozedurales Beispiel: Konto¶
Am besten versteht man diese Dinge an einem Beispiel.
Stellen wir uns einmal vor, wir würden für eine Bank ein System für die Verwaltung von Konten entwickeln, das das Anlegen neuer Konten, Überweisungen sowie Ein- und Auszahlungen ermöglicht.
Ein möglicher Ansatz sähe so aus, dass wir für jedes Bankkonto ein Dictionary anlegen, in dem dann alle Informationen über den Kunden und seinen Finanzstatus gespeichert sind. Um die gewünschten Operationen zu unterstützen, würden wir Funktionen definieren. Ein Dictionary für ein stark vereinfachtes Konto könnte folgendermaßen aussehen:
konto = { "Inhaber" : "Hans Meier", "Kontonummer" : 5671, "Kontostand" : 12000.0, }Mit unserem jetzigen Wissen könnte ein prozedurales Programm wie folgt aussehen (
oop_konto_dict.py
):def neues_konto(inhaber, kontonummer, kontostand): print(".. neues Konto :", inhaber) return { "Inhaber" : inhaber, "Kontonummer" : kontonummer, "Kontostand" : kontostand } def ueberweisung(quelle, ziel, betr): print(".. Transfer :", quelle["Inhaber"], "->", ziel["Inhaber"], betr) quelle["Kontostand"] -= betr ziel["Kontostand"] += betr def einzahlen(konto, betrag): print(".. Einzahlen :", konto["Inhaber"], betrag) konto["Kontostand"] += betrag def auszahlen(konto, betrag): print(".. Auszahlen :", konto["Inhaber"], betrag) konto["Kontostand"] -= betrag def zeige_konto(konto): print(".. Konto :", konto["Inhaber"]) print(" Kontonummer :", konto["Kontonummer"]) print(" Kontostand :", konto["Kontostand"]) if __name__ == '__main__': print("\nKontobeispiel mit dict") k1 = neues_konto("Heinz Meier", 1234, 12000.0) k2 = neues_konto("Erwin Schmidt", 6789, 15000.0) ueberweisung(k1, k2, 100) auszahlen(k1, 200) einzahlen(k2, 500) zeige_konto(k1) zeige_konto(k2)Führt man dieses Programm mit
python konto_dict.py
aus erhält manKontobeispiel mit dict .. neues Konto : Heinz Meier .. neues Konto : Erwin Schmidt .. Transfer : Heinz Meier -> Erwin Schmidt 100 .. Auszahlen : Heinz Meier 200 .. Einzahlen : Erwin Schmidt 500 .. Konto : Heinz Meier Kontonummer : 1234 Kontostand : 11700.0 .. Konto : Erwin Schmidt Kontonummer : 6789 Kontostand : 15600.0Erklärung dazu:
- Das Hauptprogramm startet mit
if __name__ == '__main__':
. Dieser Anweisungskörper wird nur ausgeführt wenn das File mitpython konto_dict.py
gestartet wird (=Hauptprogramm). Im Falle eines Imports d.h.import konto_dict.py
würde alles nach demif
nicht ausgeführt werden.- Zunächst erzeugt man zwei Konten
k1
undk2
.- Die Funktion
neues_konto
retourniert ein Dictionary mit den entsprechenden Einträgen bei der Erzeugung.- Die Funktion
ueberweisung
benötig ein Quell- und Zielkonto. Diese erhöht bzw. erniedrigt den Kontostand.- Die beiden Funktionen
einzahlen
undauszahlen
machen dies an einem Konto.- Mit
zeige_konto
kann man sich die Kontodaten ausgeben lassen.Bemerkung
Jede Funktion hat ein
Die Banksimulation arbeitet wie erwartet. Sie weist aber einige unschöne Eigenheiten auf:
- Bei den Funktionsaufrufen müssen immer Daten übergeben werden. Problematisch bei großen Datenmengen.
- Der Kontostand (
dict
) kann im Hauptprogramm direkt verändert werden ohne dass man dies merkt. Diese umgeht man mit der Datenkapselung bei der OOP (siehe unten).- Man kann Kontos nicht zusammenzuführen z.B.
k1+k2
. In der OOP möglich und bekannt als Polymorphismus (Überladung des+
Operators).- Es ist nicht möglich von einem Basiskonto andere Kontotypen wie Sparkonto, Girokonto, etc abzuleiten. Dies kennt man als Vererbung in der OOP .
Im Folgenden wird diese Objektorientierung am gleichen Beispiel erklärt.
8.2. Klassen¶
8.2.1. Konto Beispiel OOP¶
Zu Beginn des objektorientierten Designs zeichnet man i.A. die Klassen graphisch mittels UML (Unified Modelling Language) auf: Die Abbildung Konto zeigt dies für unser Beispiel.
Konto
ist dabei der Klassenname. Danach folgen die Attribute (Daten) und Methoden (Funktionen).
Basierend darauf würde eine Prototyp-Klasse in Python wie folgt aussehen (oop_konto_pass.py
):
class Konto:
def __init__(self, inhaber, kontonummer, kontostand):
""" Konstruktor """
self.Inhaber = inhaber
self.Kontonummer = kontonummer
self.Kontostand = kontostand
def ueberweisung(self, ziel, betrag):
pass
def einzahlen(self, betrag):
pass
def auszahlen(self, betrag):
pass
def zeige_konto(self):
pass
Durch die pass
Anweisung kann man die Methoden angeben, ohne diese implementieren zu müssen.
Als ersten Schritt führen Sie dieses Skript in Spyder mittels run
aus, um die Klasse interaktiv verwendbar zu haben. Es erscheint in der IPython-Konsole:
In [1]: runfile('C:/Users/.../src/oop_konto_pass.py', wdir='C:/Users/.../src')
Anmerkungen zum Beispiel:
Die Definition einer Klasse startet mit
class
und dem Namen der Klasse.Die erste Methode
__init__
ist der sogenannte Konstruktor. Die beiden__
deuten auf eine spezielle Python Methode hin. Diese wird aufgerufen wenn ein neues Objekt dieser Klasse z.B. in der IPython-Konsole erzeugt wird z.B. mit:In [2]: k1 = Konto("Heinz Meier", 1234, 12000.0) In [3]: k1 Out[3]: <__main__.Konto at 0x2360f8a7f48>
und erledigen i.A. erforderliche Variablenzuweisungen.
Danach kommen (eingerückt) die Methoden. Diese werden mit
objektname.methodename
aufgerufen z.B. mitIn [4]: k1.auszahlen(200)
Anmerkung: durch die
pass
Anweisung wird der Kontostand nicht verändert.Jede Funktionsdefinition hat einen
self
-Parameter. Dieser übergibt automatisch eine Referenz auf die Instanz bei einem Methodenaufruf. Bei:In [5]: k1.auszahlen(300)
werden der Funktion zwei Argumente (
k1
und200
) übergeben.k1
entspricht dabeiself
. In Sprachen wie C++ nennt man diesen Zeigerthis
-pointer.Der Konstruktor setzt die Attribute (Member) der Klasse. Diese sind öffentlich (public) und man kann diese anzeigen und verändern:
In [6]: k1.Inhaber Out[6]: 'Heinz Meier' In [7]: k1.Inhaber = "Karli" # neuer Inhaber In [8]: k1.Inhaber Out[8]: 'Karli'
Um dieses Attribute (Member) wirklich Privat (private) zu machen, müssen im Konstruktor die Attributenamen mit
__
beginnen d.h.:self.__Inhaber = inhaber
Das vollständige mit privaten Attributen Kontobeispiel in objektorientiertem Design lautet (oop_konto_class_einfach.py
):
class Konto:
""" Beispiel eines einfachen Kontos """
def __init__(self, inhaber, kontonummer, kontostand):
""" Konstruktor """
self.__Inhaber = inhaber
self.__Kontonummer = kontonummer
self.__Kontostand = kontostand
def ueberweisung(self, ziel, betrag):
""" Mach eine Ueberweisung """
print(".. Transfer :", self.__Inhaber, "->", ziel.__Inhaber, betrag)
self.__Kontostand -= betrag
ziel.__Kontostand += betrag
def einzahlen(self, betrag):
""" Mach eine Einzahlung """
print(".. Einzahlen :", self.__Inhaber, betrag)
self.__Kontostand += betrag
def auszahlen(self, betrag):
""" Mach eine Auszahlung """
print(".. Auszahlen :", self.__Inhaber, betrag)
self.__Kontostand -= betrag
def zeige_konto(self):
""" Zeige die Kontodaten am Bildschirm """
print(".. Konto :", self.__Inhaber)
print(" Kontonummer :", self.__Kontonummer)
print(" Kontostand :", self.__Kontostand)
if __name__ == '__main__':
print("\nKontobeispiel mit class")
# Erzeuge zwei Konto-Objekte
k1 = Konto("Heinz Meier", 1234, 12000.0)
k2 = Konto("Erwin Schmidt", 6789, 15000.0)
# Mach was damit ...
k1.ueberweisung(k2, 100)
k1.auszahlen(200)
k2.einzahlen(500)
# Zeige Kontodaten am Bildschirm
k1.zeige_konto()
k2.zeige_konto()
und erzeugt folgende Ausgabe:
Kontobeispiel mit class
.. Transfer : Heinz Meier -> Erwin Schmidt 100
.. Auszahlen : Heinz Meier 200
.. Einzahlen : Erwin Schmidt 500
.. Konto : Heinz Meier
Kontonummer : 1234
Kontostand : 11700.0
.. Konto : Erwin Schmidt
Kontonummer : 6789
Kontostand : 15600.0
Anmerkungen zum Beispiel:
Alle Members sind privat. Nur so hat man eine Datenkapselung!
Nach der Klassen- bzw. Methodendefinition steht ein Docstring. Diesen kann man anzeigen mit
In [2]: k1.__doc__ Out[2]: ' Beispiel eines einfachen Kontos '
oder
In [3]: k1.einzahlen.__doc__ Out[3]: ' Mach eine Einzahlung '
Dadurch kann man den Code gut dokumentieren!
8.2.2. Set und Get Methoden¶
Angenommen man möchte nur den Namen des Kontoinhabers abfragen bzw. dieser möchte seinen Namen ändern.
Dazu benötigt man sogenannte set
und get
Methoden. Man erweitert die obige Klasse mit (siehe oop_konto_class_getset.py
):
def inhaber(self):
""" Gibt den Namen des Inhabers zurueck """
print(".. getter wird aufgerufen")
return self.__Inhaber
def setInhaber(self, neuer_Inhaber):
""" Aendert den Namen des Inhabers """
print(".. setter wird aufgerufen")
self.__Inhaber = neuer_Inhaber
und kann dadurch auf die Attribute zugreifen (nachdem das Skript in Spyder ausgeführt wurde).
In [2]: k1.inhaber()
.. getter wird aufgerufen
Out[2]: 'Dipl.-Ing. Hans Meier'
In [3]: k1.setInhaber("Heinz Meier")
.. setter wird aufgerufen
In [4]: k1.inhaber()
.. getter wird aufgerufen
Out[4]: 'Heinz Meier'
property-Attribut :
Um das Ganze etwas eleganter zu machen, kann man beide Funktionen mit
property
„zusammenhängen“ (vollständiges Beispiel sieheoop_konto_class_property.py
):class Konto(): def inhaber(self): """ Gibt den Namen des Inhabers zurueck """ print(".. getter wird aufgerufen") return self.__Inhaber def setInhaber(self, neuer_Inhaber): """ Aendert den Namen des Inhabers """ print(".. setter wird aufgerufen") self.__Inhaber = neuer_Inhaber # Property-Attribut Inhaber = property(inhaber, setInhaber)Der Zugriff sieht dann folgendermaßen aus:
In [2]: k1 = Konto("Heinz Meier", 1234, 12000.0) In [3]: k1.Inhaber .. getter wird aufgerufen Out[3]: 'Heinz Meier' In [4]: k1.Inhaber = "Dipl.-Ing. Heinz Meier" .. setter wird aufgerufen In [5]: k1.Inhaber .. getter wird aufgerufen Out[5]: 'Dipl.-Ing. Heinz Meier'Bemerkung
Man könnte nun sagen dies ist das Gleiche als mit einem public Attribut. Der Unterschied: es wird die
Inhaber()
bzw.setInhaber()
Methode aufgerufen. Darin könnte man z.B. Zugriffsberechtigungen definieren!
8.2.3. Statische Attribute¶
Alle bislang kennengelernten Attribute waren dynamisch, d.h. diese werden bei der Objekterzeugung bzw. Entfernung automatisch erzeugt bzw. gelöscht.
Möchte man im vorliegenden Beispiel einen Konto-Zähler einbauen welcher die Anzahl der Objekte speichert,
braucht man ein statisches Attribut (siehe oop_konto_class_static.py
):
class Konto():
""" Beispiel eines Kontos """
# Statischer Zaehler
Anzahl = 0
def __init__(self, inhaber, kontonummer, kontostand):
""" Konstruktor, Aufruf bei Instanzierung """
self.__Inhaber = inhaber
self.__Kontonummer = kontonummer
self.__Kontostand = kontostand
Konto.Anzahl += 1 # Instanzzaehler erhoehen
def __del__(self):
""" Destruktor, Aufruf bei del """
Konto.Anzahl -= 1
Anmerkungen zum Beispiel:
- Das Attribut
Anzahl
steht direkt nach derclass
Definition außerhalb derdef
Anweisungen. - Die Methode
__del__
ist der sogenannte Destruktor. Dieser wird beim Löschen eines Objektes aufgerufen. - Der Destruktor wir i.A. nicht implementiert, da Python alle Attribute automatisch löscht. Im Falle des Zählers ist diese Definition aber notwendig.
Somit hat man einen statischen Zähler implementiert und kann diesen ausprobieren. Das Hauptprogramm dazu lautet:
if __name__ == '__main__':
print("\nKontobeispiel mit class & statischen Attributen")
# Erzeuge zwei Konto-Objekte
k1 = Konto("Heinz Meier", 1234, 12000.0)
k2 = Konto("Erwin Schmidt", 6789, 15000.0)
k3 = Konto("Susi Jung", 9147, 9000.0)
# Anzahl bestehender Konten
print(".. Kontoanzahl :", Konto.Anzahl)
# Loesche ein Konto
print("-> Loesche ")
k3.zeige_konto()
del(k3)
# neue Anzahl
print(".. Kontoanzahl :", Konto.Anzahl)
Startet man das Skript oop_konto_class_static.py
erhält man:
Kontobeispiel mit class & statischen Attributen
.. Kontoanzahl : 3
-> Loesche
.. Konto : Susi Jung
Kontonummer : 9147
Kontostand : 9000.0
.. Kontoanzahl : 2
8.3. Vererbung¶
Im folgenden wird das obige Programm erweitert, um das Prinzip der Vererbung darzustellen. Es gibt bekanntlich unterschiedliche Kontotypen (Girokonto, Sparkonto, …). Alle haben
- Gemeinsamkeiten aber auch
- unterschiedliche Eigenschaften.
Dieses Verhalten kann man durch Vererbung realisieren. Die Abbildung Vererbung zeigt dies für unser Beispiel.
Der dazugehörige vereinfachte Code lautet (oop_konto_vererbung.py
):
class Konto:
""" Basis Konto Klasse """
def __init__(self, inhaber, kontonummer, kontostand):
""" Konstruktor, Aufruf bei Instanzierung """
print(".. Konto anlegen")
self.Inhaber = inhaber
self.Kontonummer = kontonummer
self.Kontostand = kontostand
self.zeige_konto()
def einzahlen(self, betrag):
""" Mach eine Einzahlung """
print(f".. Einzahlen : {self.Kontonummer} {betrag:.1f}")
self.Kontostand += betrag
def auszahlen(self, betrag):
""" Mach eine Auszahlung """
print(f".. Auszahlen : {self.Kontonummer} {betrag:.1f}")
self.Kontostand -= betrag
def zeige_konto(self):
""" Zeige die Kontodaten am Bildschirm """
print(".. Konto :", self.Inhaber)
print(" Kontonummer :", self.Kontonummer)
print(" Kontostand :", self.Kontostand)
class Girokonto(Konto):
""" Giro Konto Klasse """
def __init__(self, inhaber, kontonummer, kontostand, sollzinsen, habenzinsen):
""" Giro Konstruktor, Aufruf bei Instanzierung """
self.__Sollzinsen = sollzinsen
self.__Habenzinsen = habenzinsen
# initialisiere Konto
Konto.__init__(self, inhaber, kontonummer, kontostand)
def ueberweisung(self, ziel, betrag):
""" Mach eine Ueberweisung """
print(f".. Transfer : {self.Kontonummer} -> {ziel.Kontonummer} {betrag:.1f}")
self.Kontostand -= betrag
ziel.Kontostand += betrag
class Sparkonto(Konto):
""" Sparbuch Konto Klasse """
def __init__(self, inhaber, kontonummer, kontostand, zinssatz):
""" Spar Konstruktor, Aufruf bei Instanzierung """
self.Zinssatz = zinssatz
# initialisiere des Kontos
Konto.__init__(self, inhaber, kontonummer, kontostand)
def zeige_konto(self):
""" Zeige die Kontodaten am Bildschirm, ueberschreibt Konto Funktion """
Konto.zeige_konto(self)
print(" Zinssatz :", self.Zinssatz)
if __name__ == '__main__':
print("\nKontobeispiel mit Vererbung")
# Erzeuge zwei Konto-Objekte
kg = Girokonto("Heinz Meier", 78340, 12000.0, 0.05, 0.01)
ks = Sparkonto("Heinz Meier", 78341, 4000.0, 0.03)
print("------------------------------")
# Mach was damit ...
kg.einzahlen(1000)
ks.einzahlen(2000)
kg.auszahlen(300)
kg.ueberweisung(ks, 100)
print("------------------------------")
# Zeige Kontodaten am Bildschirm
kg.zeige_konto()
ks.zeige_konto()
Dieses Programm liefert folgenden Output:
Kontobeispiel mit Vererbung
.. Konto anlegen
.. Konto : Heinz Meier
Kontonummer : 78340
Kontostand : 12000.0
.. Konto anlegen
.. Konto : Heinz Meier
Kontonummer : 78341
Kontostand : 4000.0
Zinssatz : 0.03
-------------------------
.. Einzahlen : 78340 1000.0
.. Einzahlen : 78341 2000.0
.. Auszahlen : 78340 300.0
.. Transfer : 78340 -> 78341 100.0
-------------------------
.. Konto : Heinz Meier
Kontonummer : 78340
Kontostand : 12600.0
.. Konto : Heinz Meier
Kontonummer : 78341
Kontostand : 6100.0
Zinssatz : 0.03
Anmerkungen zum Beispiel:
- Durch die Klassendefinition
class Neueklasse(Basisklasse)
werden der neuen Klasse alle Methoden der Basisklasse vererbt. - Im Konstruktor-Aufruf der neuen Klasse wird mit
Konto.__init__()
der Konstruktor der Basisklasse aufgerufen. - Die Methoden
einzahlen()
undauszahlen()
wurden vererbt und nicht neu implementiert. - Nur die Klasse
Girokonto
hat eineueberweisung()
Funktion. - Bei der Klasse
Sparkonto
wurde exemplarisch diezeige_konto()
Methode überschrieben. Gleiches könnte man auch bei der KlasseGirokonto
machen. - Set und Get Methoden wurden der Übersichtlichkeit halber nicht implementiert.
8.4. Polymorphismus¶
Polymorphismus (griechisch, „Vielgestaltigkeit“) ist ein Konzept in der Programmierung
- mit ein und der selben Funktion (z.B.
print
) bzw. selben Operatoren (+
,``-,``*
,…) - verschiedene Datentypen verwenden zu können.
Hier ein einfaches Beispiel (oop_vektor.py
):
class Vektor:
""" 2D Vektor Klasse """
def __init__(self, x, y):
self.x = x
self.y = y
def __add__(self, other):
""" Ueberladener '+' Operator """
return Vektor( self.x+other.x, self.y+other.y )
def __str__(self):
""" Ueberladene print Funktion """
return f"[{self.x} {self.y}]"
if __name__ == '__main__':
print("\nBeispiel Polymorphismus")
# Erzeuge zwei Vektor Objekte
v1 = Vektor(3,4)
v2 = Vektor(1,2)
# Addiere 2 Vektoren
vs = v1 + v2
# Ausgabe des neuen Vektors
print('vs =', vs)
mit der zugehörigen Ausgabe:
Beispiel Polymorphismus
vs = [4 6]
Anmerkungen zum Beispiel:
Der
__add__
Operator (+
) und__str__
(steuertprint
Ausgabe) werden überlagert.Dies erlaubt in wenigen Zeilen die Definition eines eigenen Datentyps
Vektor
.Sowie die Verwendung von
+
undprint
.Es gibt eine Vielzahl von sogenannten Magic Members welche überladen werden können. Dazu gehören z.B.:
__init__() __del__() __str__() __iter__() __add__() __sub__() __mul__() __gt__()
8.5. Übungsbeispiele¶
Aufgabe 8.1
Schreiben Sie eine Klasse Vektor
, die einen Vektor beliebiger Dimension speichern kann. Als Klassenattribute sollen eine Liste Liste
und die Dimension der Liste Dim
gespeichert werden.
- Der Konstruktor bekommt eine Liste übergeben und initialisiert damit die Attribute
Liste
undDim
. - Definieren Sie eine Methode
norm()
, die die euklidische Norm des Vektors berechnet:
Überladen Sie den Additions-Operator (
__add__
), sodass 2 Vektoren addiert werden und als Resultat ein neuer Vektor zurück gegeben wird. Die Addition soll für beliebige Dimensionen funktionieren. Sie können annehmen, dass die beiden Vektoren die selbe Dimension haben. z.B.:In [1]: v = Vektor([1, 2]) + Vektor([3, 4]) In [2]: print(v.Liste, v.Dim) [4, 6] 2
Machen Sie das Attribut
Dim
privat. Was müssen Sie dadurch zusätzlich an Ihrem Programm ändern?
Testen Sie Ihre Klasse auch mit Vektoren größerer Dimensionen!
Aufgabe 8.2
Schreiben Sie eine Klasse Kraft
, die die Masse m
und den Beschleunigungsvektor a
übergeben bekommt und den daraus resultierenden Kraftvektor berechnet (f = m * a). Als Attribute sollen
- die Masse
m
(Gleitkommazahl), - der Beschleunigungsvektor
a
(Liste beliebiger Dimension) - die resultierende Kraft
Liste
(Liste beliebiger Dimension) und - die Dimension
Dim
vonListe
(Ganzzahl)
gespeichert werden.
- Dazu soll die Klasse
Kraft
von der KlasseVektor
abgeleitet werden: Sie soll alle Attribute und Methoden von der KlasseVektor
erben. Zusätzlich sollen die AttributeListe
undDim
über den Konstruktor der KlasseVektor
gesetzt werden. - Überladen Sie den print-Operator (
__str__
), sodass die Masse, der Beschleunigungsvektor und der Kraftvektor ausgegeben werden. - Überprüfen Sie die Ergebnisse der Methode
norm()
und des überladenen Additions-Operators der KlasseKraft
.
8.6. weitere Übungsbeispiele¶
Aufgabe 8.3
Modifizieren Sie die Lösung von Aufgabe 8.1 so, dass auch der Multiplikations-Operator (__mul__
) überladen wird. Das Produkt Vektor1 * Vektor2
soll dabei das Skalarprodukt der Instanzen Vektor1
und Vektor2
berechnen. z.B.:
In[1]: print(Vektor([1, 2]) * Vektor([4, 6]))
16
Aufgabe 8.4
Erstellen Sie aufbauend auf den Funktionen aus Aufgabe 7.1 eine Klasse:
- Definieren Sie eine Klasse mit passendem Namen. Die Klasse soll die Datenstruktur zum Speichern der Länder (z.B. ein Dictionary) als Klassenattribut enthalten. Dazu soll ein Konstruktor ohne Parameter definiert werden, der das Dictionary initialisiert.
- Wandeln Sie alle Funktionen von Aufgabe 7.1 zu Klassenmethoden um.
- Überladen Sie den print-Operator, sodass das Dictionary der Klasse direkt mittels print auf eine Klasseninstanz ausgegeben werden kann.
Aufgabe 8.5
Erstellen Sie eine Klasse
Person
mit den AttributenName
(string) undAlter
(integer). Die Attribute sollen über den Konstruktor gesetzt und als private (!) Variablen gespeichert werden. z.B.:In [1]: Anton = Person("Anton Mueller", 23)
Schreiben Sie Get-Methoden, über die das Alter und der Name der Person abgerufen werden können. Schreiben Sie eine Methode zum Setzen des Alters der Person. Überprüfen Sie in einem Abschnitt
main
, dass tatsächlich nicht direkt auf die Attribute Alter und Name zugegriffen werden kann. z.B. für die oben definierte PersonAnton
:In [2]: print(Anton.Get_Alter()) 23 In [3]: print(Anton.Get_Name()) Anton Mueller In [4]: Anton.Set_Alter(40) In [5]: print(Anton.Get_Alter()) 40
Erstellen Sie eine von
Person
abgeleitete KlasseStudent
, welche neben den AttributenAlter
undName
noch das zusaetzliche AttributMatrikelnummer
(= string) hat. Die Matrikelnummer soll NICHT als private Variable gespeichert werden! Die Attribute sollen in einem Konstruktor gesetzt werden. Verwenden Sie zum Initialisieren der AttributeAlter
undName
den Konstruktor der BasisklassePerson
! z.B.:In [6]: Student_Anton = Student("Anton Mueller", 23, "1110111")
Überladen Sie für die Klasse
Student
den print-Operator, so dass Name, Alter und Matrikelnummer des Studenten ausgegeben werden. Verwenden Sie zum Abrufen des Namens und Alters die fuer 2.) geschriebenen Get-Methoden. z.B. für den oben definierten StudentenStudent_Anton
:In [7]: print(Student_Anton) Name: Anton Mueller, Alter: 23, Matrikelnummer: 1110111