Skip to content
Snippets Groups Projects
Commit 464a7035 authored by Malte Woidt's avatar Malte Woidt
Browse files

Lösung für Übung3

parent 99c51b68
No related branches found
No related tags found
No related merge requests found
%% Cell type:markdown id:f0b13807-b3b2-4f55-9581-9f8d4ffaa3b5 tags: %% Cell type:markdown id:f0b13807-b3b2-4f55-9581-9f8d4ffaa3b5 tags:
### **Einleitendes Thema - Struktur und Übersicht mittels Objektorientierter Programmierung** ### **Einleitendes Thema - Struktur und Übersicht mittels Objektorientierter Programmierung**
Objektorientierte Programmierung folgt der Idee, zusammengehörige Daten (also Variablen) gemeinsam mit Funktionalität in Klassen zusammenzufassen.\ Objektorientierte Programmierung folgt der Idee, zusammengehörige Daten (also Variablen) gemeinsam mit Funktionalität in Klassen zusammenzufassen.\
Dabei werden mehrere Ziele verfolgt. Zum einen ergibt sich eine einfachere Möglichkeit ein Programm in logische Teilaspekte zu zerlegen. Wir haben mit den Listen schon eine Klasse kennengelernt. Bei der Objektorientierten Programmierung können wir uns eigene Klassen nach belieben definieren. Nehmen wir an, wir wollen die Beziehung mehrerer Menschen untereinander in einem Programm abbilden. Jeder Mensch hat einen Namen, ein Alter, vieleicht Kinder und kann verheiratet sein. Das sind 4 Variablen für jeden Menschen, die zusammengehören. Immer wenn ein weiterer Mensch diesem Programm hinzugefügt wird, müssen diese 4 Variablen definiert werden. Es wäre daher sinvoll, sie auch zusammen zu gruppieren. Das ermöglichen Klassen. Eine Klasse ist eine Art Schablone, z.B. für einen Menschen. Erschaffen wir in unserem Programm einen konkreten Menschen, nennt man diesen eine Instanz der Klasse Mensch. Die 4 Variablen, die zusammengefasst diesen Menschen beschreiben nennt man dann Attribute. Einer Klasse können auch sogenannte Methoden zugeordnet werden. Das sind Funktionen, die spezifisch etwas mit dieser speziellen Klasse machen. Z.B. hat die Klasse ```list``` eine Methode ```append```, die ihr schon kennengelernt habt Dabei werden mehrere Ziele verfolgt. Zum einen ergibt sich eine einfachere Möglichkeit ein Programm in logische Teilaspekte zu zerlegen. Wir haben mit den Listen schon eine Klasse kennengelernt. Bei der Objektorientierten Programmierung können wir uns eigene Klassen nach belieben definieren. Nehmen wir an, wir wollen die Beziehung mehrerer Menschen untereinander in einem Programm abbilden. Jeder Mensch hat einen Namen, ein Alter, vieleicht Kinder und kann verheiratet sein. Das sind 4 Variablen für jeden Menschen, die zusammengehören. Immer wenn ein weiterer Mensch diesem Programm hinzugefügt wird, müssen diese 4 Variablen definiert werden. Es wäre daher sinvoll, sie auch zusammen zu gruppieren. Das ermöglichen Klassen. Eine Klasse ist eine Art Schablone, z.B. für einen Menschen. Erschaffen wir in unserem Programm einen konkreten Menschen, nennt man diesen eine Instanz der Klasse Mensch. Die 4 Variablen, die zusammengefasst diesen Menschen beschreiben nennt man dann Attribute. Einer Klasse können auch sogenannte Methoden zugeordnet werden. Das sind Funktionen, die spezifisch etwas mit dieser speziellen Klasse machen. Z.B. hat die Klasse ```list``` eine Methode ```append```, die ihr schon kennengelernt habt
%% Cell type:markdown id:78480fdf-603c-48e5-b671-5b634c94fbaa tags: %% Cell type:markdown id:78480fdf-603c-48e5-b671-5b634c94fbaa tags:
### **Kompaktes Fallbeispiel - Mensch (human)** ### **Kompaktes Fallbeispiel - Mensch (human)**
%% Cell type:code id:58bf74f7-2ed4-49bc-b9a6-cc5ce29bf7a4 tags: %% Cell type:code id:58bf74f7-2ed4-49bc-b9a6-cc5ce29bf7a4 tags:
``` python ``` python
#Klasse #Klasse
class Human(object):#Eine Klasse für Menschen class Human(object):#Eine Klasse für Menschen
# Klassenattribute # Klassenattribute
numberOfPeople = 0 numberOfPeople = 0
# Konstruktor. Wird automatisch ausgeführt, wenn eine Instanz der Klasse erzeugt wird # Konstruktor. Wird automatisch ausgeführt, wenn eine Instanz der Klasse erzeugt wird
def __init__(self, def __init__(self,
name, name,
age): age):
# Objektattribute # Objektattribute
self.name = name self.name = name
self.age = age self.age = age
self.children = [] self.children = []
self.marriagePartner = None self.marriagePartner = None
Human.numberOfPeople += 1 Human.numberOfPeople += 1
# Klassenmethoden # Klassenmethoden
def getNumberOfPeople(): def getNumberOfPeople():
print(f"Es gibt {Human.numberOfPeople} Menschen\n") print(f"Es gibt {Human.numberOfPeople} Menschen\n")
# Objektmethoden # Objektmethoden
def givingBirth(self, def givingBirth(self,
name): name):
child=Human(name,age=0) child=Human(name,age=0)
self.children.append(child) self.children.append(child)
return child return child
def seeChildren(self): def seeChildren(self):
if self.children != []: if self.children != []:
print(f"{self.name}:") print(f"{self.name}:")
for child in self.children: for child in self.children:
print(f"{child.name} ist {child.age} Jahre alt" ) print(f"{child.name} ist {child.age} Jahre alt" )
print(" ") print(" ")
else: print(f"{self.name} hat keine Kinder\n") else: print(f"{self.name} hat keine Kinder\n")
def marry(self, marriagePartner): def marry(self, marriagePartner):
self.marriagePartner = marriagePartner self.marriagePartner = marriagePartner
marriagePartner.marriagePartner = self marriagePartner.marriagePartner = self
def seeMarriagePartner(self): def seeMarriagePartner(self):
if self.marriagePartner != None: if self.marriagePartner != None:
print(self.marriagePartner.name) print(self.marriagePartner.name)
#Spezielle Objektmethode #Spezielle Objektmethode
def __repr__(self): def __repr__(self):
outstring = f"Name: {self.name}, Alter: {self.age} Jahre" outstring = f"Name: {self.name}, Alter: {self.age} Jahre"
return outstring return outstring
def __del__(self): def __del__(self):
Human.numberOfPeople -= 1 Human.numberOfPeople -= 1
# Demonstration der Funktionalitaet # Demonstration der Funktionalitaet
jenny = Human("Jenny", 32)#Erzeugt eine Instanz der Klasse Human jenny = Human("Jenny", 32)#Erzeugt eine Instanz der Klasse Human
paul = Human("Paul", 31) paul = Human("Paul", 31)
jenny.givingBirth("Steffi")#Ruft eine Objektmethode auf (bzw. nutzt Funktionalität, die ein Mensch zur Verfügung stellt) jenny.givingBirth("Steffi")#Ruft eine Objektmethode auf (bzw. nutzt Funktionalität, die ein Mensch zur Verfügung stellt)
jenny.givingBirth("Ralph") jenny.givingBirth("Ralph")
jenny.seeChildren() jenny.seeChildren()
paul.seeChildren() paul.seeChildren()
paul.children = jenny.children paul.children = jenny.children
paul.seeChildren() paul.seeChildren()
jenny.marry(paul) jenny.marry(paul)
jenny.seeMarriagePartner() jenny.seeMarriagePartner()
paul.seeMarriagePartner() paul.seeMarriagePartner()
Human.getNumberOfPeople()#Auch die Klasse als ganzes kann sogenannte Klassenmethoden besitzen Human.getNumberOfPeople()#Auch die Klasse als ganzes kann sogenannte Klassenmethoden besitzen
# Definierte spezielle Methode __repr__ erlaubt direkte Ausgabe von Objekten des Typs Human # Definierte spezielle Methode __repr__ erlaubt direkte Ausgabe von Objekten des Typs Human
print(paul) print(paul)
``` ```
%% Output %% Output
Jenny: Jenny:
Steffi ist 0 Jahre alt Steffi ist 0 Jahre alt
Ralph ist 0 Jahre alt Ralph ist 0 Jahre alt
Paul hat keine Kinder Paul hat keine Kinder
Paul: Paul:
Steffi ist 0 Jahre alt Steffi ist 0 Jahre alt
Ralph ist 0 Jahre alt Ralph ist 0 Jahre alt
Paul Paul
Jenny Jenny
Es gibt 4 Menschen Es gibt 4 Menschen
Name: Paul, Alter: 31 Jahre Name: Paul, Alter: 31 Jahre
%% Cell type:markdown id:9837909c-0cc2-43bd-9692-bb149a7a87ba tags: %% Cell type:markdown id:9837909c-0cc2-43bd-9692-bb149a7a87ba tags:
--- ---
# **Übersicht - Objektoriente Programmierung mit Python** # **Übersicht - Objektoriente Programmierung mit Python**
In diesem Grundlagen Notebook wird ein Überblick über das Objekt orientierte Programmieren (OOP) gegeben. Wir beschäftigen uns mit dem Erstellen von Klassen, zusammen mit Ihren Attributen und Methoden. In diesem Grundlagen Notebook wird ein Überblick über das Objekt orientierte Programmieren (OOP) gegeben. Wir beschäftigen uns mit dem Erstellen von Klassen, zusammen mit Ihren Attributen und Methoden.
Die wichtige Konstruktormethode ```__init__(self, ...)``` wird eingeführt und es werden verschiedene Beispiele für Klassen gegeben. Die wichtige Konstruktormethode ```__init__(self, ...)``` wird eingeführt und es werden verschiedene Beispiele für Klassen gegeben.
%% Cell type:markdown id:6f8e9e7e-e6f8-4f62-a531-5912723861ff tags: %% Cell type:markdown id:6f8e9e7e-e6f8-4f62-a531-5912723861ff tags:
### **Lernziele des Notebooks** ### **Lernziele des Notebooks**
* Klassen und Objekte * Klassen und Objekte
* Attribute * Attribute
* statische Attribute/ Klassenattribute * statische Attribute/ Klassenattribute
* Für Konstanten * Für Konstanten
* default-Werte * default-Werte
* Überblick aller Objekte * Überblick aller Objekte
* Objektattribute/ Instanzvariablen * Objektattribute/ Instanzvariablen
* Methoden * Methoden
* Klassenmethoden * Klassenmethoden
* Objektmethoden * Objektmethoden
* Konstruktor * Konstruktor
* magic methods (Definition von Operatoren) * magic methods (Definition von Operatoren)
%% Cell type:markdown id:77967f78-0ab4-4922-81a2-0e05975daf87 tags: %% Cell type:markdown id:77967f78-0ab4-4922-81a2-0e05975daf87 tags:
### **Klassen und Objekte** ### **Klassen und Objekte**
Klassen kann man sich wie selbst definierte (komplexe) Datentypen vorstellen. Die Objekte sind dann Variablen dieses Datentyps. Im Fallbeispiel wurde z.B. die Klasse ```Human``` definiert, ```jenny``` und ```paul``` werden dann als Objekte dieser Klasse instanziiert. Es ist eine Konvention, Klassennamen mit einem Großbuchstaben zu beginnen, und Objektnamen mit einem Kleinbuchstaben zu beginnen. Klassen kann man sich wie selbst definierte (komplexe) Datentypen vorstellen. Die Objekte sind dann Variablen dieses Datentyps. Im Fallbeispiel wurde z.B. die Klasse ```Human``` definiert, ```jenny``` und ```paul``` werden dann als Objekte dieser Klasse instanziiert. Es ist eine Konvention, Klassennamen mit einem Großbuchstaben zu beginnen, und Objektnamen mit einem Kleinbuchstaben zu beginnen.
Für eine Klassendefinition reicht prinzipiell ein Klassenname aus Für eine Klassendefinition reicht prinzipiell ein Klassenname aus
``` python ``` python
class ExampleClass: class ExampleClass:
pass pass
``` ```
> Mit dem Stichwort ```pass``` wird hier ein Platzhalter für noch kommenden Code erzeugt, mit dem wir später der Klasse Elemente hinzufügen werden. <br> > Mit dem Stichwort ```pass``` wird hier ein Platzhalter für noch kommenden Code erzeugt, mit dem wir später der Klasse Elemente hinzufügen werden. <br>
Ein Objekt ```exampleObject``` der Klasse ```ExampleClass``` kann nun mittels Ein Objekt ```exampleObject``` der Klasse ```ExampleClass``` kann nun mittels
```python ```python
exampleObject = ExampleClass() exampleObject = ExampleClass()
``` ```
initialisiert werden. initialisiert werden.
%% Cell type:markdown id:d48b0115 tags: %% Cell type:markdown id:d48b0115 tags:
### **Attribute** ### **Attribute**
Attribute sind Variablen, die zu einer Klasse oder einem Objekt gehören. Sie können beispielsweise die Eigenschaften eines Objektes beschreiben, wie zum Beispiel in dem Fallbeispiel das Alter ```self.age```. Attribute sind Variablen, die zu einer Klasse oder einem Objekt gehören. Sie können beispielsweise die Eigenschaften eines Objektes beschreiben, wie zum Beispiel in dem Fallbeispiel das Alter ```self.age```.
%% Cell type:markdown id:a7be4b61-84a2-41b8-abfc-fbcfc2d79f5f tags: %% Cell type:markdown id:a7be4b61-84a2-41b8-abfc-fbcfc2d79f5f tags:
#### **Objektattribute - Instanzvariablen** #### **Objektattribute - Instanzvariablen**
Um den Objekten einer Klasse eigene Attribute zuzuordnen, werden Variableninitialisierungen innerhalb einer Klassendefinition in die Definition des Klassen-Konstruktors ```__init__(self, ...)``` (vgl. Abschnitt zu Methoden) geschrieben: Um den Objekten einer Klasse eigene Attribute zuzuordnen, werden Variableninitialisierungen innerhalb einer Klassendefinition in die Definition des Klassen-Konstruktors ```__init__(self, ...)``` (vgl. Abschnitt zu Methoden) geschrieben:
```python ```python
class ExampleClass: class ExampleClass:
def __init__(self, exampleValue): # Konstruktordefinition def __init__(self, exampleValue): # Konstruktordefinition
self.exampleObjectAttribute = exampleValue # Objektattribut-Initialisierung self.exampleObjectAttribute = exampleValue # Objektattribut-Initialisierung
``` ```
> Jedes Objekte der Klasse ```ExampleClass``` hat ein individuelles Objektattribute```exampleObjectAttribute```.<br> > Jedes Objekte der Klasse ```ExampleClass``` hat ein individuelles Objektattribute```exampleObjectAttribute```.<br>
> Objektattribute werden auch Instanzvariablen genannt. <br> > Objektattribute werden auch Instanzvariablen genannt. <br>
> Ein Objektattribut kann mittels der Punktnotation ```exampleObject.exampleObjectAttribute``` aufgerufen werden und so im Hauptprogramm oder auch dann innerhalb der Klasse mit self.exampleObjectAttribute verwendet werden. > Ein Objektattribut kann mittels der Punktnotation ```exampleObject.exampleObjectAttribute``` aufgerufen werden und so im Hauptprogramm oder auch dann innerhalb der Klasse mit self.exampleObjectAttribute verwendet werden.
%% Cell type:markdown id:cd73425a-507d-4c1f-8cb2-68b31a5a5540 tags: %% Cell type:markdown id:cd73425a-507d-4c1f-8cb2-68b31a5a5540 tags:
#### **Klassenattribute - statische Attribute** #### **Klassenattribute - statische Attribute**
Um einer gesamten Klasse Attribute zuzuordnen, werden Variableninitialisierungen innerhalb der Klassendefinition geschrieben: Um einer gesamten Klasse Attribute zuzuordnen, werden Variableninitialisierungen innerhalb der Klassendefinition geschrieben:
```python ```python
class ExampleClass: class ExampleClass:
exampleClassAttribute = 0 # Klassenattribut exampleClassAttribute = 0 # Klassenattribut
``` ```
> Das Klassenattribut ```exampleClassAttribute``` teilen sich alle Objekte der Klasse ```ExampleClass```.<br> > Das Klassenattribut ```exampleClassAttribute``` teilen sich alle Objekte der Klasse ```ExampleClass```.<br>
> Klassenattribute werden auch statische Attribute genannt. <br> > Klassenattribute werden auch statische Attribute genannt. <br>
> Das Klassenattribute kann mittels ```ExampleClass.exampleClassAttribute``` aufgerufen werden und so dann innerhalb der Klasse oder auch im Hauptprogramm von allen Objekten der Klasse verwendet werden. > Das Klassenattribute kann mittels ```ExampleClass.exampleClassAttribute``` aufgerufen werden und so dann innerhalb der Klasse oder auch im Hauptprogramm von allen Objekten der Klasse verwendet werden.
%% Cell type:markdown id:5b8aef64-e792-4cf5-a4f9-60f11af3d453 tags: %% Cell type:markdown id:5b8aef64-e792-4cf5-a4f9-60f11af3d453 tags:
#### **Methoden** #### **Methoden**
Methoden sind Funktionen einer Klasse, die auf die Klassen- und Objektattribute zugreifen können. Sie werden genutzt, um mit der Klasse verbundene Operationen durchzuführen. Methoden sind Funktionen einer Klasse, die auf die Klassen- und Objektattribute zugreifen können. Sie werden genutzt, um mit der Klasse verbundene Operationen durchzuführen.
Der Konstruktor ```__init__(self, ...)``` ist beispielsweise eine Methode seiner Klasse und wird bei der Initialisierung eines Objektes von diesem aufgerufen. Der Konstruktor ```__init__(self, ...)``` ist beispielsweise eine Methode seiner Klasse und wird bei der Initialisierung eines Objektes von diesem aufgerufen.
Es können jedoch auch eigene Methoden formuliert werden. Es können jedoch auch eigene Methoden formuliert werden.
Dazu werden mit "Funktionsdefinitionen" innerhalb der Klassendefinition neue Methoden erzeugt. Dazu werden mit "Funktionsdefinitionen" innerhalb der Klassendefinition neue Methoden erzeugt.
Eine Funktion, deren Definition innerhalb einer Klassendefinition steht, wird somit Methode genannt und wird auch anders verwendet als eine reguläre Funktion: Eine Funktion, deren Definition innerhalb einer Klassendefinition steht, wird somit Methode genannt und wird auch anders verwendet als eine reguläre Funktion:
```python ```python
class ExampleClass: class ExampleClass:
exampleClassAttribute = 0 # Klassenattribut exampleClassAttribute = 0 # Klassenattribut
def __init__(self, exampleValue): # Konstruktordefinition def __init__(self, exampleValue): # Konstruktordefinition
self.exampleObjectAttribute = exampleValue self.exampleObjectAttribute = exampleValue
def exampleClassMethod(): # Klassenmethodendefinition def exampleClassMethod(): # Klassenmethodendefinition
ExampleClass.exampleClassAttribute += 1 ExampleClass.exampleClassAttribute += 1
def exampleObjectMethod(self): def exampleObjectMethod(self):
self.exampleObjectAttribute = 10 self.exampleObjectAttribute = 10
``` ```
%% Cell type:markdown id:4b39618d-5a2c-4310-b4d9-a67e05ecc4b0 tags: %% Cell type:markdown id:4b39618d-5a2c-4310-b4d9-a67e05ecc4b0 tags:
#### **Punktnotation: Methodenaufrufe und Attributzugriffe** #### **Punktnotation: Methodenaufrufe und Attributzugriffe**
Methoden können von Objekten, oder manchmal auch Klassen, aufgerufen werden. Methoden können von Objekten, oder manchmal auch Klassen, aufgerufen werden.
Dazu wird die "dot-notation" verwendet, also die Punktnotation. Dazu wird die "dot-notation" verwendet, also die Punktnotation.
Möchte man mit einem Objekt ```exampleObject``` der Klasse ```ExampleClass``` die Methode ```exampleObjectMethod()``` der Klasse ```ExampleClass``` aufrufen, geschieht das mit der Punktnotation: ```exampleObject.exampleObjectMethod()```. Möchte man mit einem Objekt ```exampleObject``` der Klasse ```ExampleClass``` die Methode ```exampleObjectMethod()``` der Klasse ```ExampleClass``` aufrufen, geschieht das mit der Punktnotation: ```exampleObject.exampleObjectMethod()```.
Dabei wird eine Referenz, auf das aufrufende Objekt selber, lokal beim Methodenaufruf in ```self``` gespeichert. Dabei wird eine Referenz, auf das aufrufende Objekt selber, lokal beim Methodenaufruf in ```self``` gespeichert.
Während nun die Methode ```exampleObjectMethod()``` ausgeführt wird, steht somit das Objekt ```exampleObject``` über ```self``` weiterhin zur Verfügung. Während nun die Methode ```exampleObjectMethod()``` ausgeführt wird, steht somit das Objekt ```exampleObject``` über ```self``` weiterhin zur Verfügung.
Es können somit weitere Methoden aufgerufen werden oder es kann auch auf Attribute zugegriffen werden. Es können somit weitere Methoden aufgerufen werden oder es kann auch auf Attribute zugegriffen werden.
Das passiert ohne, dass beim Schreiben der Methoden bekannt sein musste, welche Objekte später diese Methode aufrufen werden. Das passiert ohne, dass beim Schreiben der Methoden bekannt sein musste, welche Objekte später diese Methode aufrufen werden.
Da erst bei Ausführung der Methode die Referenz des aufrufenden Objektes in ```self``` lokal gespeichert wird.\ Da erst bei Ausführung der Methode die Referenz des aufrufenden Objektes in ```self``` lokal gespeichert wird.\
Auch auf die Attribute eines Objektes wird mit der Punktnotation zugegriffen. Auch auf die Attribute eines Objektes wird mit der Punktnotation zugegriffen.
Entweder mit dem Objekt direkt selber ```exampleObject.exampleObjectAttribute``` oder über die lokale Referenz ```self.exampleObjectAttribute```. Entweder mit dem Objekt direkt selber ```exampleObject.exampleObjectAttribute``` oder über die lokale Referenz ```self.exampleObjectAttribute```.
%% Cell type:markdown id:e62551be-2e37-4a4f-9416-4d0f69eac24c tags: %% Cell type:markdown id:e62551be-2e37-4a4f-9416-4d0f69eac24c tags:
#### **Klassenmethoden** #### **Klassenmethoden**
Ist der Inhalt einer Methode unabhängig von dem Objekt das sie aufruft, dann wird/muss kein ```(self...)``` in der Parameterliste übergeben werden. Ist der Inhalt einer Methode unabhängig von dem Objekt das sie aufruft, dann wird/muss kein ```(self...)``` in der Parameterliste übergeben werden.
Es handelt sich bei einer Methode um eine Klassenmethode, wenn kein ```(self...)``` in der Parameterliste vorhanden ist. Es handelt sich bei einer Methode um eine Klassenmethode, wenn kein ```(self...)``` in der Parameterliste vorhanden ist.
Bei Klassenmethoden werden keine Attribute eines aufrufenden Objektes verwendet oder bearbeitet, da die Information eines aufrufenden Objektes, also das ```self```, nicht vorhanden sind.\ Bei Klassenmethoden werden keine Attribute eines aufrufenden Objektes verwendet oder bearbeitet, da die Information eines aufrufenden Objektes, also das ```self```, nicht vorhanden sind.\
Klassenmethoden können über den Klassennamen aufgerufen werden: ```ExampleClass.exampleClassMethod()```. Klassenmethoden können über den Klassennamen aufgerufen werden: ```ExampleClass.exampleClassMethod()```.
Jedoch nicht über Objekte der Klasse: ~```exampleObject.exampleClassMethod()```~. Bei Ersterem war nie die Information eines aufrufenden Objektes von Interesse. Bei Letzterem findet die Information, über das aufrufende Objekt, keinen passenden Parameter in der Klassenmethodendefinition, wo diese Info gespeichert werden könnte, sodass die Methoden-Signatur nicht zum Methoden-Aufruf passt. Jedoch nicht über Objekte der Klasse: ~```exampleObject.exampleClassMethod()```~. Bei Ersterem war nie die Information eines aufrufenden Objektes von Interesse. Bei Letzterem findet die Information, über das aufrufende Objekt, keinen passenden Parameter in der Klassenmethodendefinition, wo diese Info gespeichert werden könnte, sodass die Methoden-Signatur nicht zum Methoden-Aufruf passt.
```python ```python
class ExampleClass: class ExampleClass:
exampleClassAttribute = 0 # Klassenattribut exampleClassAttribute = 0 # Klassenattribut
def __init__(self, exampleValue): # Konstruktordefinition def __init__(self, exampleValue): # Konstruktordefinition
self.exampleObjectAttribute = exampleValue self.exampleObjectAttribute = exampleValue
def exampleClassMethod(): # Klassenmethodendefinition def exampleClassMethod(): # Klassenmethodendefinition
ExampleClass.exampleClassAttribute += 1 ExampleClass.exampleClassAttribute += 1
def exampleObjectMethod(self): # Objektmethodendefinition def exampleObjectMethod(self): # Objektmethodendefinition
self.exampleObjectAttribute = 10 self.exampleObjectAttribute = 10
ExampleClass.exampleClassMethod() # Klassenmethodenaufruf mittels Klasse ExampleClass.exampleClassMethod() # Klassenmethodenaufruf mittels Klasse
``` ```
%% Cell type:markdown id:45900822-0657-41b7-8807-a42541c81043 tags: %% Cell type:markdown id:45900822-0657-41b7-8807-a42541c81043 tags:
#### **Objektmethoden** #### **Objektmethoden**
Objektmethoden müssen bei der Definition in ihrer Parameterliste immer als erstes Argument eine Referenz auf das Objekt selbst haben, um auf dessen Elemente zugreifen zu können. Diese Referenz ist ```self```. Bei Aufruf der Objektmethode, wird das ```self``` automatisch übergeben und wird daher nicht explizit in der Parameterliste angegeben. Die Methode ```exampleObjectMethof(self)``` dem Beispiel aus dem vorherigen Abschnitt würde also im Programm folgendermaßen aufgerufen: Objektmethoden müssen bei der Definition in ihrer Parameterliste immer als erstes Argument eine Referenz auf das Objekt selbst haben, um auf dessen Elemente zugreifen zu können. Diese Referenz ist ```self```. Bei Aufruf der Objektmethode, wird das ```self``` automatisch übergeben und wird daher nicht explizit in der Parameterliste angegeben. Die Methode ```exampleObjectMethof(self)``` dem Beispiel aus dem vorherigen Abschnitt würde also im Programm folgendermaßen aufgerufen:
```python ```python
exampleObject = exampleClass() exampleObject = exampleClass()
exampleObject.exampleObjectMethod() exampleObject.exampleObjectMethod()
``` ```
Es ist übrigends nicht nötig, die Referenz auf das eigene Objekt ```self``` zu nennen. Es ist einfach immer der erste Parameter der Objektmethode. Die Benennung ist allerdings eine Konvention und es ist sinnvoll sich daran zu halten Es ist übrigends nicht nötig, die Referenz auf das eigene Objekt ```self``` zu nennen. Es ist einfach immer der erste Parameter der Objektmethode. Die Benennung ist allerdings eine Konvention und es ist sinnvoll sich daran zu halten
%% Cell type:markdown id:fd40aee3 tags: %% Cell type:markdown id:fd40aee3 tags:
#### **Spezielle Methoden - Magic methods** #### **Spezielle Methoden - Magic methods**
Klassen kann man sich wie einen selbst definierten Datentypen vorstellen. Datentypen, die wir bereits kennen, können oft mit Operatoren interagieren. Dabei hängt es vom Datentyp ab, was der Operator genau tut. Z.B. können zwei Listen durch ein + aneinandergehängt werden. Ein solches Verhalten kann man auch mit selbst definierten Klassen erreichen. Neben Operatoren gibt es auch verschiedene vordefinierte Funktionen, die ähnliches Verhalten zeigen. Z.B. ```len()``` oder ```print```.\ Klassen kann man sich wie einen selbst definierten Datentypen vorstellen. Datentypen, die wir bereits kennen, können oft mit Operatoren interagieren. Dabei hängt es vom Datentyp ab, was der Operator genau tut. Z.B. können zwei Listen durch ein + aneinandergehängt werden. Ein solches Verhalten kann man auch mit selbst definierten Klassen erreichen. Neben Operatoren gibt es auch verschiedene vordefinierte Funktionen, die ähnliches Verhalten zeigen. Z.B. ```len()``` oder ```print```.\
Um die Verwendung von Operatoren zusammen mit eigenen Klassen zu ermöglichen müssen spezielle Objektmethoden programmiert werden, sogenannte "magic methods". Betrachten wir die Programmzeile\ Um die Verwendung von Operatoren zusammen mit eigenen Klassen zu ermöglichen müssen spezielle Objektmethoden programmiert werden, sogenannte "magic methods". Betrachten wir die Programmzeile\
```a + b``` ```a + b```
Falls a keine einfache Zahl ist (dort funktioniert der Mechanismus etwas anders) wird diese Zeile automatisch ersetzt durch: Falls a keine einfache Zahl ist (dort funktioniert der Mechanismus etwas anders) wird diese Zeile automatisch ersetzt durch:
```a.__add__(b)``` ```a.__add__(b)```
Also durch einen Methodenaufruf. Sofern die Klasse, von der a eine Instanz ist, diese Objektmethode implementiert, kann sie zusammen mit Operatoren verwendet werden. Das funktioniert nicht nur für das +. Eine Tabelle mit einigen magic methods Also durch einen Methodenaufruf. Sofern die Klasse, von der a eine Instanz ist, diese Objektmethode implementiert, kann sie zusammen mit Operatoren verwendet werden. Das funktioniert nicht nur für das +. Eine Tabelle mit einigen magic methods
|Operator | Methode| Anmerkung| |Operator | Methode| Anmerkung|
|---------|----------|----------| |---------|----------|----------|
|+ |__add__ | | |+ |__add__ | |
|- |__sub__ | | |- |__sub__ | |
|/ |__truediv__| | |/ |__truediv__| |
|* |__mul__ | | |* |__mul__ | |
|//| __floordiv__| | |//| __floordiv__| |
|% |__mod__| | |% |__mod__| |
|** |__pow__| | |** |__pow__| |
|>= | __ge__| | |>= | __ge__| |
|< | __lt__| | |< | __lt__| |
|==| __eq__| | |==| __eq__| |
| | __repr__| gibt die Zeichenkette zurück, die durch print ausgegeben wird| | | __repr__| gibt die Zeichenkette zurück, die durch print ausgegeben wird|
| |__init__ | wird automatisch beim erzeugen eines Objekts aufgerufen | | |__init__ | wird automatisch beim erzeugen eines Objekts aufgerufen |
| |__del__ | wird automatisch aufgerufen, wenn das Objekt gelöscht wird | | |__del__ | wird automatisch aufgerufen, wenn das Objekt gelöscht wird |
| [] | __getattr__(self,index),__setattr__(self,index,val) | wird durch den Indexoperator aufgerufen| | [] | __getitem__(self,index),__setitem__(self,index,val) | wird durch den Indexoperator aufgerufen|
| | __len__| wird aufgerufen, um die Länge des Objektes mit len zu ermitteln| | | __len__| wird aufgerufen, um die Länge des Objektes mit len zu ermitteln|
Da die Methoden Objektmethoden sind muss ihr erster Parameter das angesprochene ```self``` sein. Weitere Parameter kommen je nach Operator hinzu. Alle Rechenoperatoren sind z.B. binäre Operatoren. Die magic methods haben einen zweiten Parameter, der das Objekt auf der rechten Seite des + enthält Da die Methoden Objektmethoden sind muss ihr erster Parameter das angesprochene ```self``` sein. Weitere Parameter kommen je nach Operator hinzu. Alle Rechenoperatoren sind z.B. binäre Operatoren. Die magic methods haben einen zweiten Parameter, der das Objekt auf der rechten Seite des + enthält
Die verfügbaren Magic methods sind fest bestimmt (siehe Liste ganz unten auf https://www.tutorialsteacher.com/python/magic-methods-in-python), und beginnen und enden immer mit zwei Unterstrichen ```__```. Die bereits bekannte Methode ```__init__()``` gehört demnach auch zu diesen Methoden. Im Fallbeispiel oben ist dies beispielhaft für die Verwendung in ```print( )``` gezeigt. Das ist die Methode ```__repr__(self)```. Die verfügbaren Magic methods sind fest bestimmt (siehe Liste ganz unten auf https://rszalski.github.io/magicmethods/#operators), und beginnen und enden immer mit zwei Unterstrichen ```__```. Die bereits bekannte Methode ```__init__()``` gehört demnach auch zu diesen Methoden. Im Fallbeispiel oben ist dies beispielhaft für die Verwendung in ```print( )``` gezeigt. Das ist die Methode ```__repr__(self)```.
%% Cell type:markdown id:20a2b2ac-6118-46fd-ba80-771d9e10dc64 tags: %% Cell type:markdown id:20a2b2ac-6118-46fd-ba80-771d9e10dc64 tags:
> <font color='grey'>*Weiter Informationen zu Klassen können in der [offiziellen Dokumentation von Python](https://docs.python.org/3/tutorial/classes.html) nachgelesen werden*</font> > <font color='grey'>*Weiter Informationen zu Klassen können in der [offiziellen Dokumentation von Python](https://docs.python.org/3/tutorial/classes.html) nachgelesen werden*</font>
%% Cell type:markdown id:97f327d5-f6f9-4c84-ab4a-c2627003cf60 tags: %% Cell type:markdown id:97f327d5-f6f9-4c84-ab4a-c2627003cf60 tags:
#### **Vererbung** #### **Vererbung**
Vererbung ist ein Feature, das in Python nicht so häufig verwendet wird. Es ist möglich aus selbst definierten Klassen ganze Hierarchien aufzubauen. Um die Idee zu verdeutlichen konnen wir auf das Beispiel mit der Klasse Human zurück. Man könnte sagen ein Student ist auch ein Mensch, ein Mensch ist aber nicht zwingend ein Student. Ein Student teilt alle Merkmale eines Menschen, kann aber zusätzliche Attribute und Methoden besitzen. In Python kann diese Beziehung folgendermaßen ausgedrückt werden (Die Zelle funktioniert nur, wenn du die Zelle am Anfang des Notebooks ausführst, da die Klasse Human definiert sein muss) Vererbung ist ein Feature, das in Python nicht so häufig verwendet wird. Es ist möglich aus selbst definierten Klassen ganze Hierarchien aufzubauen. Um die Idee zu verdeutlichen konnen wir auf das Beispiel mit der Klasse Human zurück. Man könnte sagen ein Student ist auch ein Mensch, ein Mensch ist aber nicht zwingend ein Student. Ein Student teilt alle Merkmale eines Menschen, kann aber zusätzliche Attribute und Methoden besitzen. In Python kann diese Beziehung folgendermaßen ausgedrückt werden (Die Zelle funktioniert nur, wenn du die Zelle am Anfang des Notebooks ausführst, da die Klasse Human definiert sein muss)
%% Cell type:code id:880f9feb-225f-4e98-ac22-5baa0039404c tags: %% Cell type:code id:880f9feb-225f-4e98-ac22-5baa0039404c tags:
``` python ``` python
class Student(Human):#Die runde Klammer bedeutet, dass die Klasse Student alle Attribute und Methoden von der Klasse Human erbt class Student(Human):#Die runde Klammer bedeutet, dass die Klasse Student alle Attribute und Methoden von der Klasse Human erbt
def __init__(self, name, age, semester=None):#Einzelne Methoden können in der Klasse Student neu definiert werden. Z.B. der Konstruktor def __init__(self, name, age, semester=None):#Einzelne Methoden können in der Klasse Student neu definiert werden. Z.B. der Konstruktor
super().__init__(name, age) #Damit die Klasse korrekt funktioniert muss der Original-Konstruktor von Human aufgerufen werden super().__init__(name, age) #Damit die Klasse korrekt funktioniert muss der Original-Konstruktor von Human aufgerufen werden
if semester != None: if semester != None:
self.semester = semester self.semester = semester
else: self.semester = 0 else: self.semester = 0
def marry(self, marriagePartner): def marry(self, marriagePartner):
print ("Ich habe gerade zu viel zu tun") print ("Ich habe gerade zu viel zu tun")
#Einige Funktionalität dazu #Einige Funktionalität dazu
jan=Student("Jan",21,3) jan=Student("Jan",21,3)
Human.getNumberOfPeople()#Auch der Student Jan zählt zu den Menschen Human.getNumberOfPeople()#Auch der Student Jan zählt zu den Menschen
print(jan) #Alle Methoden die nicht überschrieben wurden von der Klasse Human übernommen print(jan) #Alle Methoden die nicht überschrieben wurden von der Klasse Human übernommen
jan.marry(jenny) #Diese Methode wurde überschrieben und verhält sich anders (Das ist auch gut so, jenny ist ja schon mit Paul verheiratet) jan.marry(jenny) #Diese Methode wurde überschrieben und verhält sich anders (Das ist auch gut so, jenny ist ja schon mit Paul verheiratet)
``` ```
%% Output %% Output
Es gibt 5 Menschen Es gibt 5 Menschen
Name: Jan, Alter: 21 Jahre Name: Jan, Alter: 21 Jahre
Ich habe gerade zu viel zu tun Ich habe gerade zu viel zu tun
%% Cell type:markdown id:908d1c92-ed0b-4633-a6a1-f8475ec45b57 tags: %% Cell type:markdown id:908d1c92-ed0b-4633-a6a1-f8475ec45b57 tags:
Zusammengefasst erbt die neue Klasse (auch sub-Klasse) sämtliche Methoden von der Elternklasse (super-Klasse). Werden Methoden in der sub-Klasse neu definiert, dann werden diese Methoden sozusagen überschrieben. Das alleine würde wenig neue Funktionalität bieten. Die Klasse Student definiert z.B. die Methode ```__init__``` neu. Deshalb wird die Methode der Klasse Human auch nicht mehr aufgerufen, wenn die Klasse Student instanziiert wird. Gleiches gilt für die Methode ```mary```.\ Zusammengefasst erbt die neue Klasse (auch sub-Klasse) sämtliche Methoden von der Elternklasse (super-Klasse). Werden Methoden in der sub-Klasse neu definiert, dann werden diese Methoden sozusagen überschrieben. Das alleine würde wenig neue Funktionalität bieten. Die Klasse Student definiert z.B. die Methode ```__init__``` neu. Deshalb wird die Methode der Klasse Human auch nicht mehr aufgerufen, wenn die Klasse Student instanziiert wird. Gleiches gilt für die Methode ```mary```.\
Es ist allerdings trotzdem möglich die Methoden gleichen Namens der super-Klasse aufzurufen. Das funktioniert mit ```super().Methode()```, wie in der ```__init``` Methode von Student gezeigt. Gerade wenn die ```__init__``` Methode in einer sub-Klasse neu definiert wird sollte genau diese Zeile an der ersten Stelle stehen, damit das Konzept der Vererbung wirklich funktioniert. Wird die ```__del``` Methode in einer sub-Klasse neu definiert, sollte ein ```super().__del__()``` als letzte Zeile dieser Methode eingeplant werden. Die meißten anderen Programmiersprachen machen genau das automatisch, bei Python muss man selbst daran denken. Praktisch gesehen ist Vererbung ein Konzept, dem ihr vermutlich in Python nicht so häufig begegnen werdet. Ihr könnt damit eigentlich nur bestehende Klassen nachträglich erweitern. Es ist allerdings trotzdem möglich die Methoden gleichen Namens der super-Klasse aufzurufen. Das funktioniert mit ```super().Methode()```, wie in der ```__init``` Methode von Student gezeigt. Gerade wenn die ```__init__``` Methode in einer sub-Klasse neu definiert wird sollte genau diese Zeile an der ersten Stelle stehen, damit das Konzept der Vererbung wirklich funktioniert. Wird die ```__del``` Methode in einer sub-Klasse neu definiert, sollte ein ```super().__del__()``` als letzte Zeile dieser Methode eingeplant werden. Die meißten anderen Programmiersprachen machen genau das automatisch, bei Python muss man selbst daran denken. Praktisch gesehen ist Vererbung ein Konzept, dem ihr vermutlich in Python nicht so häufig begegnen werdet. Ihr könnt damit eigentlich nur bestehende Klassen nachträglich erweitern.
......
%% Cell type:markdown id:757b9a6c-6599-4cd6-93c5-c4fb30ed5b53 tags:
# <font color='blue'>**Übung 3 - Objektorientierte Programmierung**</font>
%% Cell type:markdown id:d3a49027-9186-48a6-ba9f-332428e93bd9 tags:
## <font color='blue'>**Die Grundlagen von Objektorientierter Programmierung**</font>
In den vorherigen Übungen wurden die grundlegenden Elemente zur Ablaufsteuerung von Programmen behandelt. In dieser Übung führen wir ein wichtiges Konzept der Programmierung ein, die Objektorientierte Programmierung. Diese hilft sowohl bei der Strukturierung komplexer Projekte, der Verständlichkeit von Quellcode und dem gemeinsamen Arbeiten. Die Grundidee dahinter ist es, zusammengehörende Daten zusammen mit dazu passenden Operationen in (selbst) definierten Datentypen zusammenzufassen.
### **Weitere Notebooks, die dir helfen könnten**
* Python Grundlagen Teil 1
* Python Grundlagen Teil 2
* OOP Grundlagen
### **Vorkenntnisse**
* Übung 1
* Übung 2
### **Lernziele**
* Klassen und Objekte
* Attribute und Methoden
* Spezielle Methoden
%% Cell type:markdown id:b0e426c9-9a66-4bc6-a0be-fc8e08c18858 tags:
# <font color='blue'>**Abschnitt 1 - Einfache Klasse**</font>
## <font color='blue'>*Aufgabe*</font>
Für den ersten Kontakt mit Klassen und Objekten wollen wir mit einem einfachen Alltagsbeispiel beginnen.
Erstelle einen Datentyp, mit dem ein Supermarkt seine Artikel verwalten kann. Die Artikel sollen einen Namen, eine Menge und einen Preis pro Mengeneinheit haben. Dazu soll die Datenstruktur Methoden besitzen, mit denen der Supermarkt den Lagerbestand auffüllen kann, sowie eine bestimmte Menge eines Artikels (soweit vorhanden) verkaufen kann. Beim Verkaufen soll eine Ausgabe über die verkaufte Menge, den Preis und die verbleibende Menge gegeben werden. Außerdem soll ermittelbar sein, wieviele verschiedene Artikel es gibt.
Teste das Programm anhand eines Supermarktes der zu Beginn 500 kg Zucker zu 1 Euro pro kg, und 1000 kg Mehl zu 1,50 Euro pro kg auf Lager hat. Er füllt das Lager um 600 kg Zucker auf, und ein Kunde kauft 4 kg Mehl. Nutze die erstellte Klasse, um die neuen Lagerbestände zu ermitteln, sowie den vom Kunden gezahlten Preis.
%% Cell type:markdown id:b29c819a-aedd-4232-bcd7-eedcd810695c tags:
## <font color='blue'>*Lösung*</font>
Klassendefinition und Instanziierung:
%% Cell type:code id:6b0c8c9d-f3b6-47fc-9de7-04f557ed5511 tags:
``` python
class Artikel:#definiert eine Klasse namens artikel
anz_artikel = 0 #Eine Klassenvariable, die mitzählt, wie viele verschiedene Objekte der Klasse Artikel existieren
def __init__(self, name, menge, preis):#Der Konstruktor der Klasse. Wird immer aufgerufen, wenn ein Objekt der Klasse erzeugt wird
self.name = name#legt eine Objektvariable an, die den Namen enthalten soll, der dem Konstruktor übergeben wurde
if menge > 0:#überprüft, ob die angegebene Startmenge größer 0 ist
self.menge = menge#wenn ja wird eine Objektvariable angelegt, die die Startmenge enthält
else:
self.menge = 0#Falls die angegebene Menge nicht größer 0 war, wird die Startmenge auf 0 gesetzt. Es soll keine negative menge möglich sein
self.preis = preis#Der Preis des Artikels wird auch in objektvariablen gespeichert
Artikel.anz_artikel += 1#Die Anzahl der existierenden Artikel um eins erhöhen
def auffuellen(self, menge):#Eine Objektmethode, die die menge der Waren auffüllt
self.menge += menge#addiert die aufzufüllende Menge auf die bestehende Menge des Artikels
def verkaufen(self, verkaufsmenge):#Eine Objektmethode zum Verkaufen
if verkaufsmenge > self.menge:#Falls mehr verkauft werden soll als überhaupt vorhanden ist
verkaufsmenge = self.menge#die Verkaufsmenge auf die gerade verfügbare Menge reduzieren
self.menge -= verkaufsmenge#die Verkaufsmenge von der Vorratsmenge des Artikels abziehen
print(f"{verkaufsmenge} kg {self.name} fuer {self.preis*verkaufsmenge} Euro verkauft. Es verbleiben {self.menge} kg auf Lager.")#Ausgabe was verkauft wurde und wie viel es gekostet hat. Diese Schreibweise für Zeichenketten ist sehr praktisch, wenn man viele Zahlen einbinden möchte
def __del__(self):#der Destruktor. Wird aufgerufen, wenn ein Objekt gelöscht wird
Artikel.anz_artikel-=1#Die Gesamtzahl der vorhandenen Artikel muss um eins heruntergezählt werden, da ein Artikel gerade gelöscht wird
zucker = Artikel("Zucker", 500., 1.)#Erzeugt zucker. Ein Objekt der Klasse Artikel. Die Parameter sind nötig, da __init__ von Artikel 3 Parameter benötigt (+ das self, das bei Objektmethodenaufrufen implizit der Parameterliste hinzugefügt wird)
mehl = Artikel("Mehl", 1000., 1.5)
```
%% Cell type:markdown id:d046ee6a tags:
Auffüll- und Verkaufsoperationen, sowie Testausgaben (beliebig oft wiederholbar):
%% Cell type:code id:7b68c5f0 tags:
``` python
zucker.auffuellen(600)#Ruft eine Objektmethode auf. Der Parameter self der Funktion auffuellen wird hierbei mit dem Objekt zucker initialisiert, da zucker vor dem Punkt steht
mehl.verkaufen(4)#Das gleiche fügr Mehl mit der verkaufen-Methode
print(f"Bestand Zucker: {zucker.menge} kg")#Auf Objektvariablen kann mit dem punkt zugegriffen werden. zucker.menge ist die Variable menge des Objetkes zucker
print(f"Bestand Mehl: {mehl.menge} kg")
```
%% Output
4 kg Mehl fuer 6.0 Euro verkauft. Es verbleiben 996.0 kg auf Lager.
Bestand Zucker: 1100.0 kg
Bestand Mehl: 996.0 kg
%% Cell type:code id:59fd42a8-6344-40a7-af32-5be4e24f2fa1 tags:
``` python
#Ein kurzes Beispiel um dir noch einmal die Funktion von Python Variablen zu verdeutlichen
print("Anzahl der definierten Artikel",Artikel.anz_artikel)# Es gibt 2 Artikel, mehl und zucker.
mehl2=mehl#Erzeugt keinen neuen Artikel mehl2 und mehl verweisen auf das selbe Objekt der Klasse Artikel
print("Anzahl der definierten Artikel",Artikel.anz_artikel)# Es gibt immer noch 2 Artikel. mehl2 und mehl sind der identische Artikel
mehl=2#Belegt die Variable mehl mit einem neun Wert bzw. Objekt. Einer 2
print("Anzahl der definierten Artikel",Artikel.anz_artikel)# Es gibt immer noch 2 Artikel. mehl2 und zucker. Da das "Mehl"-Objekt mit mehl2 einen zweiten Namen hat wurde es in der letzten Zeile nicht gelöscht
mehl2=4#Die Variable mehl2 wird jetzt auch neu belegt. Für das "Mehl"-Objekt gibt es jetzt keinen Variablennamen mehr. Es wird dadurch gelöscht
print("Anzahl der definierten Artikel",Artikel.anz_artikel)# Es gibt nur noch einen Artikel. Den Zucker
```
%% Output
Anzahl der definierten Artikel 2
Anzahl der definierten Artikel 2
Anzahl der definierten Artikel 2
Anzahl der definierten Artikel 1
%% Cell type:markdown id:c665968c tags:
## <font color='blue'>*Hinweise*</font>
Es soll eine Klasse und ein Hauptprogramm erstellt werden. In dem Hauptprogramm sollen Objekte der Klasse verwendet werden, um die beispielhafte Testaufgabe zu erfüllen. Natürlich kannst du dich hier auch frei austoben und mehrere Verkäufe machen, oder testen, ob die Klasse beim Verkaufen korrekt erkennt, wenn nicht genug auf Lager ist. Es darf keine negativen Mengen geben.
Wir machen folgende Vorschläge für die Namen der Klasse, Attribute und Methoden:
Klasse ```Artikel```,
Attribute ```anz_artikel```, ```menge```, ```preis```, ```name```
Methoden ```__init__()```, ```auffuellen()```, ```verkaufen()```
Für die Syntax benötigst du Informationen aus dem Grunlagen OOP Notebook. Für diesen Abschnitt benötigst du Klassen und Objekte, Klassen- und Objektattribute, sowie Objektmethoden.
%% Cell type:markdown id:46d21f3a-b37e-49d3-864f-587b09ba0b89 tags:
# <font color='blue'>**Abschnitt 2 - Vektorrechnungen**</font>
Nachdem du nun erste Erfahrungen mit der Objektorientierten Programmierung gemacht hast, wollen wir uns einem mathematischeren Thema widmen. Dabei wollen wir auch das Thema spezielle Methoden (auch magic method genannt) einführen. Diese erlauben es uns in dieser Aufgabe, Vektorrechnung mit den bekannten Operatoren wie mit "normalen" Variablen auch, durchzuführen.
%% Cell type:markdown id:6e25b06f tags:
## <font color='blue'>*Aufgabe*</font>
Berechne den Betrag des Vektors c = a-b, mit den Vektoren a = (1,2,3) und b = (5,-4,3). Außerdem soll das Skalarprodukt a * b berechnet werden. Erstelle dazu eine Klasse ```Vektor3``` für die dreidimensionalen Vektoren so, dass die bereits gegebenen Codezeilen erfolgreich ausgeführt werden können.
%% Cell type:markdown id:e7333454-be6d-487d-a39e-ba509472aa30 tags:
## <font color='blue'>*Lösung*</font>
%% Cell type:code id:dc85e62c-a188-46ab-b7c5-55fc44d8bbbf tags:
``` python
class Vektor3:#Eine Klasse Vektor
def __init__(self, coords):
if len(coords)!= 3:#Überprüft, ob der parameter coords 3 Werte enthält. Das kann eine Liste oder ein Tupel sein
raise ValueError('Invalid input.')#Falls nicht kann so ein Fehler ausgelöst werden. Ihr könnt es selbst ausprobieren, indem ihr versucht einen Vektor3 aus einer Liste mit mehr als 3 Einträgen zu erzeugen
self.x = coords[0]#Setzt für x,y und z Objektvariablen
self.y = coords[1]
self.z = coords[2]
def get_betrag(self):#Eine Methode, um die Länge des Vektors zu berechnen
return (self.x**2 + self.y**2 + self.z**2)**0.5
def __mul__(self, vec2):#Eine magic method, die Aufgerufen wird, wenn ein Vec3 auf der linken Seite des * zeichens steht
res = (self.x * vec2.x) + (self.y * vec2.y) + (self.z * vec2.z)
return res
def __sub__(self, vec2):#das gleiche für +
coords_c = [self.x - vec2.x, self.y - vec2.y, self.z - vec2.z]
c = Vektor3(coords_c)
return c
#Ein Paar kleine Extras, nur zum Zeigen
def __len__(self):#Falls len() mit einem Vector3-Objekt aufgerufen wird, kommt das Ergebnis 3 heraus. Der Vec3 ist 3 Elemente lang
return 3
def __getitem__(self,index):#durch diese Funktion kann der Indexoperator verwendet werden, um Werte aus dem Vektor zu bekommen. Nicht ganz perfekt da jeder index größer 2 das z zurückliefert
if index==0:
return self.x
elif index==1:
return self.y
else:
return self.z
def __setitem__(self,index,value):#durch diese Methode kann man auch Elementen des Vektors mit dem Indexoperator Werte zuweisen
if index==0:
self.x=value
elif index==1:
self.y=value
else:
self.z=value
a = Vektor3([1,2,3])
b = Vektor3([5,-4,3])
c = a-b
print("Betrag des Vektors c: " + str(c.get_betrag()))
print("Skalarprodukt a * b: " + str(a*b))
```
%% Output
Betrag des Vektors c: 7.211102550927978
Skalarprodukt a * b: 6
%% Cell type:code id:a2987eb5-b92f-47fd-b32e-eefeb02d09f7 tags:
``` python
#Ein paar kleine Extras
print(a[0])#Durch das __getitem__ funktioniert der Zugriff auf die einzelnen Komponenten des Vektors
a[0]=4#In diesem Fall wird a.__setitem__(4) aufgerufen
print (a[0])
```
%% Output
1
4
%% Cell type:markdown id:9dfb2bc6-c09d-4109-a999-db7f73aee7c9 tags:
## <font color='blue'>*Weitere Operatoren*</font>
Eine vollständige Liste sämtlicher magic-functions von Python Objekten findet ihr https://rszalski.github.io/magicmethods/#operators
Auch das in der Vorlesung angesprochene Problem, dass in Falle des Vektor3 ein Aufruf eines Operators nur funktioniert, wenn das Vector3-Objekt auf der linken Seite steht, also
```
a=Vector3([1,2,3])
b= a+4 #wird zu a.__add__(4)
b= 4+a #Funktioniert nicht, da die 4 keine __add__ Methode hat, bzw. diese nicht für Objekte der Klasse Vector3 gedacht ist
```
kann über magic-functions mit Einschränkungen gelöst werden. Die entsprechende Funktion würde ```__radd__``` heißen. Näheres findet ihr unter dem Link
%% Cell type:markdown id:ac9ba52d tags:
## <font color='blue'>*Hinweise*</font>
Nun benötigst du noch den Abschnitt über spezielle Methoden aus dem OOP-Grundlagen Notebook. Diese erlauben es, die Methoden so zu definieren, dass die Operationen wie a-b mit den selbst definierten Vektoren funktionieren.
Es ist dir selbst überlassen, ob du die Einträge der Vektoren als eine Liste, oder als drei einzelne Werte speicherst.
Das Ergebnis einer Summe oder Differenz ist ein neuer Vektor.
%% Cell type:markdown id:595a83db-5a61-4048-84d3-e5f1ca14c263 tags:
# <font color='blue'>**Aufgabe zum selbst probieren**</font>
%% Cell type:markdown id:ca559a03-b79b-4f1a-a128-4e3e3e177017 tags:
Erweitere die Vektorklasse um weitere Operationen, die dir sinnvoll erscheinen. Ebenso könnte es interessant sein, die Beschränkung auf 3 Dimensionen aufzuheben. Beachte dabei, dass die Operationen bestimmte Anforderungen haben. So funktioniert das Kreuzprodukt nur mit Vektoren mit drei Dimensionen, Addition und Substraktion nur für Vektoren gleicher Dimension etc. Die Funktionen müssen das dann überprüfen und ansonsten eine Warnung ausgeben, dass die Operation keine sinnvolle Lösung hat.
## <font color='blue'>**Lösung**</font>
%% Cell type:code id:6a4e935d-1468-41be-a175-71505b044a38 tags:
``` python
```
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment