8.3 OOP in der Praxis#

Mit Klassen, Objekten und Methoden haben wir die Grundlagen der objektorientierten Programmierung kennengelernt. In diesem Kapitel schauen wir uns an, wie wir diese Konzepte in der Praxis anwenden: Wir verwalten mehrere Objekte in Listen und sorgen dafür, dass unsere Objekte sich schön ausgeben lassen.

Lernziele#

Lernziele

  • Sie können Objekte in Listen speichern und verwalten.

  • Sie können Listen von Objekten durchlaufen und durchsuchen.

  • Sie verstehen die __str__-Methode und können sie implementieren.

  • Sie können ein vollständiges objektorientiertes Programm zur Datenverwaltung erstellen.

  • Sie verstehen, wie OOP bei der Lösung praktischer Probleme hilft.

Listen von Objekten verwalten#

In der Praxis arbeiten wir selten mit nur einem Objekt. Angenommen, wir müssten die Daten aller Studierenden eines Kurses verwalten. Mit einzelnen Variablen wäre das ein Albtraum, aber mit Listen von Objekten wird es elegant und übersichtlich. Listen können nicht nur Zahlen oder Strings enthalten, sondern auch Objekte. Das macht sie zu einem mächtigen Werkzeug für die Datenverwaltung.

class Student:
    def __init__(self, nachname, vorname, studiengang, semester, matrikelnummer):
        self.nachname = nachname
        self.vorname = vorname
        self.studiengang = studiengang
        self.semester = semester
        self.matrikelnummer = matrikelnummer

# Liste mit mehreren Studierenden erstellen
studierende = [
    Student("Abendrot", "Anna", "Maschinenbau", 3, "12345678"),
    Student("Berliner", "Bob", "Maschinenbau", 5, "87654321"),
    Student("Colorado", "Charlie", "Elektrotechnik", 2, "11223344"),
    Student("Dietrich", "Diana", "Maschinenbau", 1, "99887766")
]

print(f"Anzahl Studierende: {len(studierende)}")
Anzahl Studierende: 4

Mit for-Schleifen können wir einfach über alle Objekte in der Liste iterieren:

# Alle Studierenden begrüßen
for student in studierende:
    print(f"Hallo {student.vorname} {student.nachname}!")

print()

# Nur die Namen ausgeben
for student in studierende:
    print(f"{student.vorname} {student.nachname} ({student.studiengang})")
Hallo Anna Abendrot!
Hallo Bob Berliner!
Hallo Charlie Colorado!
Hallo Diana Dietrich!

Anna Abendrot (Maschinenbau)
Bob Berliner (Maschinenbau)
Charlie Colorado (Elektrotechnik)
Diana Dietrich (Maschinenbau)

Oft müssen wir bestimmte Objekte aus einer Liste herausfiltern oder suchen:

# Nach einem bestimmten Nachnamen suchen
gesuchter_name = "Berliner"
for student in studierende:
    if student.nachname == gesuchter_name:
        print(f"Gefunden: {student.vorname} {student.nachname}")
        break

print()

# Alle Maschinenbau-Studierenden finden
maschinenbauer = []
for student in studierende:
    if student.studiengang == "Maschinenbau":
        maschinenbauer.append(student)

print("Maschinenbau-Studierende:")
for student in maschinenbauer:
    print(f"- {student.vorname} {student.nachname}")
Gefunden: Bob Berliner

Maschinenbau-Studierende:
- Anna Abendrot
- Bob Berliner
- Diana Dietrich

Mini-Übung

Schreiben Sie Code, der alle Studierenden im 3. Semester oder höher findet und deren Namen ausgibt. Verwenden Sie die bereits erstellte studierende-Liste.

# Geben Sie nach diesem Kommentar Ihren Code ein:

Lösung

# Studierende im 3. Semester oder höher
hoeheres_semester = []
for student in studierende:
    if student.semester >= 3:
        hoeheres_semester.append(student)

print("Studierende im 3. Semester oder höher:")
for student in hoeheres_semester:
    print(f"- {student.vorname} {student.nachname} ({student.semester}. Semester)")

Die str-Methode für schöne Ausgabe#

Wenn wir Objekte mit print() ausgeben, erhalten wir oft kryptische Meldungen statt nützlicher Informationen. Die __str__-Methode löst dieses Problem elegant.

Schauen wir uns an, was passiert, wenn wir ein Objekt direkt ausgeben:

class Student:
    def __init__(self, nachname, vorname, studiengang, semester, matrikelnummer):
        self.nachname = nachname
        self.vorname = vorname
        self.studiengang = studiengang
        self.semester = semester
        self.matrikelnummer = matrikelnummer

student = Student("Müller", "Max", "Maschinenbau", 3, "12345678")
print(student)  # Kryptische Ausgabe wie <__main__.Student object at 0x...>
<__main__.Student object at 0x1042b4c20>

Das ist nicht sehr hilfreich! Python zeigt nur den Objekttyp und eine Speicheradresse an.

Die __str__-Methode ist eine spezielle Methode, die Python automatisch aufruft, wenn ein Objekt als String dargestellt werden soll:

class Student:
    def __init__(self, nachname, vorname, studiengang, semester, matrikelnummer):
        self.nachname = nachname
        self.vorname = vorname
        self.studiengang = studiengang
        self.semester = semester
        self.matrikelnummer = matrikelnummer
    
    def __str__(self):
        return f"{self.vorname} {self.nachname} ({self.studiengang}, {self.semester}. Semester)"

student = Student("Müller", "Max", "Maschinenbau", 3, "12345678")
print(student)  # Jetzt: Max Müller (Maschinenbau, 3. Semester)
Max Müller (Maschinenbau, 3. Semester)

Der Unterschied wird besonders bei Listen von Objekten deutlich:

# Ohne __str__-Methode
class StudentOhne:
    def __init__(self, nachname, vorname):
        self.nachname = nachname
        self.vorname = vorname

# Mit __str__-Methode  
class StudentMit:
    def __init__(self, nachname, vorname):
        self.nachname = nachname
        self.vorname = vorname
    
    def __str__(self):
        return f"{self.vorname} {self.nachname}"

student_ohne = StudentOhne("Schmidt", "Sarah")
student_mit = StudentMit("Schmidt", "Sarah")

print("Ohne __str__:")
print(student_ohne)

print("\nMit __str__:")
print(student_mit)

# Bei Listen wird der Unterschied noch deutlicher
liste_ohne = [StudentOhne("Müller", "Max"), StudentOhne("Weber", "Lisa")]
liste_mit = [StudentMit("Müller", "Max"), StudentMit("Weber", "Lisa")]

print(f"\nListe ohne __str__: {liste_ohne}")
print(f"Liste mit __str__: {liste_mit}")
Ohne __str__:
<__main__.StudentOhne object at 0x104295970>

Mit __str__:
Sarah Schmidt

Liste ohne __str__: [<__main__.StudentOhne object at 0x1042b4c50>, <__main__.StudentOhne object at 0x1042b4c20>]
Liste mit __str__: [<__main__.StudentMit object at 0x1042b4ce0>, <__main__.StudentMit object at 0x1042b5a00>]

Eine gute __str__-Methode sollte:

  • kurz und informativ sein,

  • die wichtigsten Informationen** enthalten und

  • für Menschen lesbar sein.

Mini-Übung

Erweitern Sie die Student-Klasse um eine __str__-Methode, die den Namen und das Semester in der Form “Anna Abendrot (3. Semester)” ausgibt. Testen Sie Ihre Lösung mit mindestens zwei Objekten.

# Geben Sie nach diesem Kommentar Ihren Code ein:

Lösung

class Student:
    def __init__(self, nachname, vorname, studiengang, semester, matrikelnummer):
        self.nachname = nachname
        self.vorname = vorname
        self.studiengang = studiengang
        self.semester = semester
        self.matrikelnummer = matrikelnummer
    
    def __str__(self):
        return f"{self.vorname} {self.nachname} ({self.semester}. Semester)"

# Test der Lösung
student1 = Student("Abendrot", "Anna", "Maschinenbau", 3, "12345678")
student2 = Student("Berliner", "Bob", "Elektrotechnik", 1, "87654321")

print(student1)  # Anna Abendrot (3. Semester)
print(student2)  # Bob Berliner (1. Semester)

Ein vollständiges Beispiel: Kursverwaltung#

Jetzt bringen wir alles zusammen: Eine vollständige Student-Klasse mit allen gelernten Methoden und eine praktische Kursverwaltung, die zeigt, wie mächtig OOP in der Praxis ist.

class Student:
    def __init__(self, nachname, vorname, studiengang, semester, matrikelnummer):
        self.nachname = nachname
        self.vorname = vorname
        self.studiengang = studiengang
        self.semester = semester
        self.matrikelnummer = matrikelnummer
        self.noten = []
    
    def __str__(self):
        return f"{self.vorname} {self.nachname} ({self.studiengang}, {self.semester}. Sem)"
    
    def vollstaendiger_name(self):
        return f"{self.vorname} {self.nachname}"
    
    def note_hinzufuegen(self, note):
        self.noten.append(note)
    
    def notendurchschnitt(self):
        if len(self.noten) == 0:
            return 0.0
        return sum(self.noten) / len(self.noten)
    
    def semester_erhoehen(self):
        self.semester += 1

Wir erstellen nun den Kurs mit den teilnehmenden Studierenden und lassen alle anzeigen.

# Kurs erstellen und mit Studierenden füllen
python_kurs = [
    Student("Abendrot", "Anna", "Maschinenbau", 3, "12345678"),
    Student("Berliner", "Bob", "Maschinenbau", 5, "87654321"),
    Student("Colorado", "Charlie", "Elektrotechnik", 2, "11223344"),
    Student("Dietrich", "Diana", "Informatik", 1, "99887766"),
    Student("Eberhard", "Emil", "Maschinenbau", 4, "55443322")
]

# Alle Kursteilnehmer anzeigen
print("=== Python-Kurs Teilnehmer ===")
for student in python_kurs:
    print(f"- {student}")

print(f"\nGesamtzahl: {len(python_kurs)} Studierende")
=== Python-Kurs Teilnehmer ===
- Anna Abendrot (Maschinenbau, 3. Sem)
- Bob Berliner (Maschinenbau, 5. Sem)
- Charlie Colorado (Elektrotechnik, 2. Sem)
- Diana Dietrich (Informatik, 1. Sem)
- Emil Eberhard (Maschinenbau, 4. Sem)

Gesamtzahl: 5 Studierende

Wir können nun nach dem Studiengang filtern, das durchschnittliche Semester berechnen oder Noten verwalten.

# Nach Studiengang filtern
def studierende_nach_studiengang(studierende, studiengang):
    gefunden = []
    for student in studierende:
        if student.studiengang == studiengang:
            gefunden.append(student)
    return gefunden

maschinenbauer = studierende_nach_studiengang(python_kurs, "Maschinenbau")
print(f"\n=== Maschinenbau-Studierende ({len(maschinenbauer)}) ===")
for student in maschinenbauer:
    print(f"- {student}")

# Durchschnittliches Semester berechnen
semester_summe = 0
for student in python_kurs:
    semester_summe = semester_summe + student.semester
durchschnitt_semester = semester_summe / len(python_kurs)
print(f"\nDurchschnittliches Semester: {durchschnitt_semester:.1f}")

# Noten verwalten (Beispiel)
print("\n=== Noten eintragen ===")
python_kurs[0].note_hinzufuegen(1.7)  # Anna
python_kurs[0].note_hinzufuegen(2.3)
python_kurs[1].note_hinzufuegen(2.0)  # Bob

print(f"{python_kurs[0].vollstaendiger_name()}: Durchschnitt {python_kurs[0].notendurchschnitt():.1f}")
print(f"{python_kurs[1].vollstaendiger_name()}: Durchschnitt {python_kurs[1].notendurchschnitt():.1f}")
=== Maschinenbau-Studierende (3) ===
- Anna Abendrot (Maschinenbau, 3. Sem)
- Bob Berliner (Maschinenbau, 5. Sem)
- Emil Eberhard (Maschinenbau, 4. Sem)

Durchschnittliches Semester: 3.0

=== Noten eintragen ===
Anna Abendrot: Durchschnitt 2.0
Bob Berliner: Durchschnitt 2.0

Interessant für eine Kursverwaltung sind statistische Auswertungen.

# Hilfsfunktionen für die Kursverwaltung
def kurs_statistik(studierende):
    """Zeigt eine Übersicht über den Kurs"""
    print("=== KURS-STATISTIK ===")
    print(f"Teilnehmer gesamt: {len(studierende)}")
    
    # Maschinenbau-Studierende zählen
    maschinenbau_anzahl = 0
    for student in studierende:
        if student.studiengang == "Maschinenbau":
            maschinenbau_anzahl = maschinenbau_anzahl + 1
    
    print(f"Maschinenbau-Studierende: {maschinenbau_anzahl}")
    
    # Niedrigstes und höchstes Semester finden
    semester_min = studierende[0].semester
    semester_max = studierende[0].semester
    for student in studierende:
        if student.semester < semester_min:
            semester_min = student.semester
        if student.semester > semester_max:
            semester_max = student.semester
    
    print(f"Semester-Spanne: {semester_min}. bis {semester_max}. Semester")

def student_suchen(studierende, nachname):
    """Sucht einen Studierenden nach Nachname"""
    for student in studierende:
        if student.nachname.lower() == nachname.lower():
            return student
    return None

# Statistik anzeigen
kurs_statistik(python_kurs)

# Student suchen
print("\n=== SUCHE ===")
gefunden = student_suchen(python_kurs, "Berliner")
if gefunden:
    print(f"Gefunden: {gefunden}")
    print(f"Matrikelnummer: {gefunden.matrikelnummer}")
else:
    print("Student nicht gefunden")
=== KURS-STATISTIK ===
Teilnehmer gesamt: 5
Maschinenbau-Studierende: 3
Semester-Spanne: 1. bis 5. Semester

=== SUCHE ===
Gefunden: Bob Berliner (Maschinenbau, 5. Sem)
Matrikelnummer: 87654321

Mit wenigen Zeilen Code haben wir ein funktionsfähiges Verwaltungssystem erstellt und schließen das Beispiel mit der nächsten Mini-Übung ab.

Mini-Übung

Schreiben Sie eine Funktion erstsemester_finden(studierende), die eine Liste aller Studierenden im 1. Semester zurückgibt. Testen Sie die Funktion mit dem python_kurs.

# Geben Sie nach diesem Kommentar Ihren Code ein:

Lösung

def erstsemester_finden(studierende):
    """Findet alle Studierenden im 1. Semester"""
    erstsemester = []
    for student in studierende:
        if student.semester == 1:
            erstsemester.append(student)
    return erstsemester

# Test der Funktion
erstsemester = erstsemester_finden(python_kurs)
print(f"Erstsemester ({len(erstsemester)}):")
for student in erstsemester:
    print(f"- {student}")

Zusammenfassung und Ausblick#

Listen kombiniert mit Objekten sind ein mächtiges Werkzeug zur Verwaltung von Daten. Dabei ist es hilfreich, vordefinierte Methoden wie beispielsweise __str__ zu implementieren, um die Objekte benutzerfreundlich zu machen. Mit diesem Kapitel schließen wir die Objektorientierte Programmierung ab, um uns den Themen Matlab und Simulink zu widmen.