Skip to content

Kapitel 15: Objektorientierte Programmierung (OOP)

🎯 Lernziele

In diesem Kapitel lernst du:

  • Was Objektorientierte Programmierung (OOP) ist
  • Klassen und Objekte zu erstellen
  • Konstruktoren (__init__) zu verwenden
  • Vererbung (Inheritance) zu nutzen
  • Vorteile von OOP zu verstehen

15.1 Was ist OOP?

🤔 Warum OOP?

Problem ohne OOP (Prozedurale Programmierung):

python
# Variablen für eine Person
name = "Max"
alter = 25
email = "max@example.com"

# Funktionen
def begüß(person_name):
    print(f"Hallo {person_name}!")

begüß(name)

Lösung mit OOP:

python
class Person:
    def __init__(self, name, alter):
        self.name = name
        self.alter = alter
    
    def begüß(self):
        print(f"Hallo {self.name}!")

# Objekt erstellen
p = Person("Max", 25)
p.begüß()

🎁 Vorteile von OOP

  • Code-Organisation: Alles, was zusammengehört, ist in einer Klasse
  • Wiederverwendbarkeit: Klassen können wiederverwendet werden
  • Vererbung: Eigenschaften und Methoden können weitergegeben werden
  • Datenkapselung: Daten werden vor direktem Zugriff geschützt

15.2 Klassen und Objekte

📦 Klasse (Class)

Eine Klasse ist ein Bauplan für Objekte.

python
# Klasse definieren
class Person:
    # Klassenattribute (alle Objekte teilen sich diese)
    spezies = "Mensch"
    
    # Instanzattribute (jedes Objekt hat seine eigenen)
    def __init__(self, name, alter):
        self.name = name
        self.alter = alter
    
    # Methode
    def vorstellen(self):
        print(f"Ich heiße {self.name} und bin {self.alter} Jahre alt.")

🏠 Objekt (Instanz)

Ein Objekt ist eine Instanz einer Klasse.

python
# Objekte erstellen (Instanziierung)
p1 = Person("Max", 25)
p2 = Person("Anna", 22)

# Methoden aufrufen
p1.vorstellen()  # Ich heiße Max und bin 25 Jahre alt.
p2.vorstellen()  # Ich heiße Anna und bin 22 Jahre alt.

# Attribute zugreifen
print(p1.name)        # Max
print(p2.alter)       # 22
print(Person.spezies)  # Mensch

15.3 Der Konstruktor (__init__)

🔨 __init__()-Methode

Der Konstruktor wird automatisch aufgerufen, wenn ein Objekt erstellt wird.

python
class Person:
    def __init__(self, name, alter):
        # self bezieht sich auf das aktuelle Objekt
        self.name = name   # Instanzattribut
        self.alter = alter # Instanzattribut
        print(f"Person {name} erstellt!")

# Beim Erstellen des Objekts wird __init__() aufgerufen
p = Person("Max", 25)
# Ausgabe: Person Max erstellt!

💡 Wichtig: self

  • self bezieht sich auf das aktuelle Objekt
  • Muss als erstes Argument in Methoden stehen
  • Muss nicht beim Aufruf der Methode übergeben werden!
python
class Person:
    def __init__(self, name):  # self ist das erste Argument
        self.name = name
    
    def sag_name(self):  # self ist das erste Argument
        print(self.name)

p = Person("Max")
p.sag_name()  # Nicht p.sag_name(self) aufrufen!

15.4 Vererbung (Inheritance)

🧬 Grundlegendes Konzept

Vererbung erlaubt es, eine Klasse zu erstellen, die Eigenschaften und Methoden einer anderen Klasse übernimmt.

python
# Elternklasse (Basis-/Superklasse)
class Tier:
    def __init__(self, name):
        self.name = name
    
    def essen(self):
        print(f"{self.name} isst.")

# Kindklasse (Abgeleitete Klasse)
class Hund(Tier):  # Tier wird vererbt
    def bellen(self):
        print(f"{self.name} bellt: Wuff!")

class Katze(Tier):  # Tier wird vererbt
    def miauen(self):
        print(f"{self.name} miaut: Miau!")

# Verwenden
hund = Hund("Bello")
hund.essen()   # Geerbte Methode
hund.bellen()  # Eigene Methode

katze = Katze("Mizi")
katze.essen()   # Geerbte Methode
katze.miauen()  # Eigene Methode

🔁 Methoden überschreiben (Override)

python
class Tier:
    def geräusch(self):
        print("Tier macht ein Geräusch")

class Hund(Tier):
    def geräusch(self):  # Methode überschreiben
        print("Hund bellt: Wuff!")

class Katze(Tier):
    def geräusch(self):  # Methode überschreiben
        print("Katze miaut: Miau!")

# Polymorphismus
tiere = [Hund("Bello"), Katze("Mizi")]

for tier in tiere:
    tier.geräusch()
# Ausgabe:
# Hund bellt: Wuff!
# Katze miaut: Miau!

🔗 super() verwenden

python
class Tier:
    def __init__(self, name):
        self.name = name
        print(f"Tier {name} erstellt.")

class Hund(Tier):
    def __init__(self, name, rasse):
        super().__init__(name)  # Ruft den Konstruktor der Elternklasse auf
        self.rasse = rasse
        print(f"Hund der Rasse {rasse} erstellt.")

h = Hund("Bello", "Labrador")
# Ausgabe:
# Tier Bello erstellt.
# Hund der Rasse Labrador erstellt.

15.5 Weitere OOP-Konzepte

🔒 Datenkapselung (Encapsulation)

python
class Person:
    def __init__(self, name, alter):
        self.name = name
        self.__alter = alter  # Privates Attribut (durch __)
    
    def geburtstag(self):
        self.__alter += 1
    
    def get_alter(self):  # Getter-Methode
        return self.__alter

p = Person("Max", 25)
# print(p.__alter)  # AttributeError! (Nicht von außen zugreifbar)
p.geburtstag()
print(p.get_alter())  # 26

🎨 Polymorphismus

python
class Form:
    def fläche(self):
        pass

class Rechteck(Form):
    def __init__(self, breite, höhe):
        self.breite = breite
        self.höhe = höhe
    
    def fläche(self):
        return self.breite * self.höhe

class Kreis(Form):
    def __init__(self, radius):
        self.radius = radius
    
    def fläche(self):
        return 3.14 * self.radius ** 2

# Polymorphismus
formen = [Rechteck(5, 3), Kreis(4)]

for form in formen:
    print(f"Fläche: {form.fläche()}")

15.6 Praxisbeispiel: Bankkonto

python
class Bankkonto:
    def __init__(self, inhaber, kontonummer):
        self.inhaber = inhaber
        self.kontonummer = kontonummer
        self.__stand = 0.0  # Privates Attribut
    
    def einzahlen(self, betrag):
        if betrag > 0:
            self.__stand += betrag
            print(f"{betrag}€ eingezahlt.")
        else:
            print("Betrag muss positiv sein!")
    
    def auszahlen(self, betrag):
        if 0 < betrag <= self.__stand:
            self.__stand -= betrag
            print(f"{betrag}€ ausgezahlt.")
        else:
            print("Unzureichender Stand oder ungültiger Betrag!")
    
    def zeige_stand(self):
        print(f"Kontostand: {self.__stand:.2f}€")

# Verwenden
konto = Bankkonto("Max Mustermann", "DE123456")
konto.einzahlen(100)
konto.auszahlen(30)
konto.zeige_stand()  # Kontostand: 70.00€

⚠️ Häufige Fehler

❌ Fehler 1: self vergessen

python
# Falsch
class Person:
    def __init__(name):  # self fehlt!
        pass

# Richtig
class Person:
    def __init__(self, name):  # self ist das erste Argument
        self.name = name

❌ Fehler 2: __init__ falsch buchstabieren

python
# Falsch (wird nicht als Konstruktor erkannt)
class Person:
    def __int__(self, name):  # Falsch buchstabiert!
        pass

# Richtig
class Person:
    def __init__(self, name):  # Doppelter Unterstrich!
        self.name = name

❌ Fehler 3: Auf private Attribute von außen zugreifen

python
class Person:
    def __init__(self, name):
        self.__name = name

p = Person("Max")
# print(p.__name)  # AttributeError!

# Richtig: Getter-Methode verwenden
class Person:
    def get_name(self):
        return self.__name

📝 Zusammenfassung

In diesem Kapitel hast du gelernt:

  • ✅ Was Objektorientierte Programmierung (OOP) ist
  • ✅ Klassen und Objekte zu erstellen
  • ✅ Den Konstruktor (__init__) zu verwenden
  • ✅ Vererbung (Inheritance) zu nutzen
  • ✅ Methoden zu überschreiben
  • super() zu verwenden
  • ✅ Datenkapselung anzuwenden

🎯 Übung

  1. Erstelle eine Klasse Fahrzeug mit den Attributen marke, modell und baujahr.
  2. Erstelle eine abgeleitete Klasse Auto, die von Fahrzeug erbt und das Attribut anzahl_türen hinzufügt.
  3. Überschreibe eine Methode beschreib() in den Kindklassen.
  4. Erstelle eine Klasse Bruch mit den Attributen zähler und nenner. Implementiere Methoden für Addition und Multiplikation.

⏭️ Nächstes Kapitel

In Kapitel 16 werden wir Praxis-Projekte durchführen und das Gelernte anwenden!

Frei für alle Anfänger