Zum Inhalt springen
Python Fortgeschritten 3 min

Klassen in Python - Grundlagen

Lerne die Grundlagen der objektorientierten Programmierung in Python: Klassen, Objekte, Konstruktoren, Attribute und Methoden.

Aktualisiert:

Klassen in Python - Grundlagen

Die objektorientierte Programmierung (OOP) ist eines der wichtigsten Konzepte in der modernen Softwareentwicklung. In diesem Tutorial lernst du, wie du mit Klassen und Objekten in Python arbeitest.

Was ist OOP?

Objektorientierte Programmierung bedeutet, dass wir unseren Code um Objekte herum organisieren. Ein Objekt vereint zwei Dinge:

  • Daten (Eigenschaften/Attribute) - Was das Objekt hat
  • Verhalten (Methoden) - Was das Objekt kann

Stell dir ein Auto vor:

  • Daten: Farbe, Marke, Geschwindigkeit, Tankfuellung
  • Verhalten: fahren, bremsen, hupen, tanken

Ohne OOP wuerden wir Daten und Funktionen getrennt verwalten:

# Ohne OOP - unuebersichtlich bei vielen Autos
auto1_marke = "BMW"
auto1_farbe = "rot"
auto1_geschwindigkeit = 0

def auto_beschleunigen(geschwindigkeit, wert):
    return geschwindigkeit + wert

auto1_geschwindigkeit = auto_beschleunigen(auto1_geschwindigkeit, 50)

Mit OOP wird alles zusammengefasst und uebersichtlich:

# Mit OOP - alles gehoert zusammen
class Auto:
    def __init__(self, marke, farbe):
        self.marke = marke
        self.farbe = farbe
        self.geschwindigkeit = 0

    def beschleunigen(self, wert):
        self.geschwindigkeit += wert

mein_auto = Auto("BMW", "rot")
mein_auto.beschleunigen(50)

Klassen als Bauplan

Eine Klasse ist wie ein Bauplan oder eine Schablone. Sie beschreibt, welche Eigenschaften und Faehigkeiten Objekte haben sollen. Ein Objekt (auch Instanz genannt) ist ein konkretes Exemplar, das nach diesem Bauplan erstellt wurde.

Klasse "Auto" (Bauplan)
    ├── Attribute: marke, farbe, geschwindigkeit
    └── Methoden: beschleunigen(), bremsen()

Objekt 1: Auto("BMW", "rot")      ← konkretes Auto
Objekt 2: Auto("VW", "blau")      ← ein anderes Auto
Objekt 3: Auto("Mercedes", "schwarz") ← noch ein Auto

Jedes Objekt hat seine eigenen Daten, teilt aber den gleichen Bauplan.

Die class-Syntax

Eine Klasse wird mit dem Schluesselwort class definiert:

class MeineKlasse:
    pass  # Leere Klasse (Platzhalter)

# Objekt erstellen
objekt = MeineKlasse()
print(type(objekt))  # <class '__main__.MeineKlasse'>

Konvention: Klassennamen werden in PascalCase geschrieben (jedes Wort beginnt mit einem Grossbuchstaben): MeineKlasse, BankKonto, SpielFigur.

Der init() Konstruktor

Die Methode __init__() wird automatisch aufgerufen, wenn ein neues Objekt erstellt wird. Sie dient dazu, das Objekt mit Startwerten zu initialisieren:

class Hund:
    def __init__(self, name, rasse, alter):
        self.name = name
        self.rasse = rasse
        self.alter = alter
        self.energie = 100  # Standardwert

# Objekte erstellen
rex = Hund("Rex", "Schaeferhund", 5)
bella = Hund("Bella", "Labrador", 3)

print(rex.name)    # Rex
print(bella.alter)  # 3
print(rex.energie)  # 100

Wichtig: __init__() ist kein “echter” Konstruktor im technischen Sinne - er initialisiert das bereits erstellte Objekt. Der eigentliche Konstruktor ist __new__(), aber das ist selten relevant.

Der self-Parameter

self ist eine Referenz auf das aktuelle Objekt. Jede Methode in einer Klasse erhaelt self als ersten Parameter:

class Katze:
    def __init__(self, name):
        self.name = name  # self.name = Attribut des Objekts

    def miauen(self):
        print(f"{self.name} sagt: Miau!")

    def vorstellen(self):
        print(f"Ich bin {self.name}, eine Katze.")

mieze = Katze("Mieze")
mieze.miauen()       # Mieze sagt: Miau!
mieze.vorstellen()   # Ich bin Mieze, eine Katze.

Wenn du mieze.miauen() aufrufst, wird im Hintergrund Katze.miauen(mieze) ausgefuehrt. Python setzt self automatisch ein - du musst es nur in der Definition angeben, nicht beim Aufruf.

Attribute: Instanz vs. Klasse

Es gibt zwei Arten von Attributen:

Instanzattribute

Gehoeren zu einem einzelnen Objekt und werden in __init__() mit self. definiert:

class Student:
    def __init__(self, name, matrikelnr):
        self.name = name           # Instanzattribut
        self.matrikelnr = matrikelnr  # Instanzattribut
        self.noten = []            # Instanzattribut

anna = Student("Anna", 12345)
bob = Student("Bob", 67890)

anna.noten.append(1.3)
print(anna.noten)  # [1.3]
print(bob.noten)   # [] - Bob hat eigene Liste!

Klassenattribute

Werden von allen Objekten einer Klasse geteilt und direkt in der Klasse definiert:

class Student:
    uni = "TU Berlin"      # Klassenattribut - fuer alle gleich
    anzahl_studenten = 0   # Klassenattribut - zaehlt alle Studenten

    def __init__(self, name, matrikelnr):
        self.name = name
        self.matrikelnr = matrikelnr
        Student.anzahl_studenten += 1  # Klassenattribut aendern

anna = Student("Anna", 12345)
bob = Student("Bob", 67890)

print(Student.anzahl_studenten)  # 2
print(anna.uni)                  # TU Berlin
print(bob.uni)                   # TU Berlin

Achtung: Wenn du einem Klassenattribut ueber self einen neuen Wert zuweist, wird ein Instanzattribut erstellt, das das Klassenattribut verdeckt:

anna.uni = "FU Berlin"  # Erstellt ein Instanzattribut!
print(anna.uni)          # FU Berlin (Instanzattribut)
print(bob.uni)           # TU Berlin (immer noch Klassenattribut)
print(Student.uni)       # TU Berlin (Klassenattribut unveraendert)

Methoden definieren

Methoden sind Funktionen, die innerhalb einer Klasse definiert werden:

class Taschenrechner:
    def __init__(self):
        self.ergebnis = 0
        self.verlauf = []

    def addieren(self, zahl):
        self.ergebnis += zahl
        self.verlauf.append(f"+ {zahl}")
        return self  # Ermoeglicht Method Chaining

    def subtrahieren(self, zahl):
        self.ergebnis -= zahl
        self.verlauf.append(f"- {zahl}")
        return self

    def multiplizieren(self, zahl):
        self.ergebnis *= zahl
        self.verlauf.append(f"* {zahl}")
        return self

    def zuruecksetzen(self):
        self.ergebnis = 0
        self.verlauf.clear()

    def zeige_verlauf(self):
        print(" | ".join(self.verlauf))
        print(f"= {self.ergebnis}")

# Verwendung
rechner = Taschenrechner()
rechner.addieren(10)
rechner.multiplizieren(3)
rechner.subtrahieren(5)
rechner.zeige_verlauf()
# + 10 | * 3 | - 5
# = 25

# Method Chaining (weil wir self zurueckgeben)
rechner.zuruecksetzen()
rechner.addieren(5).multiplizieren(2).addieren(3)
print(rechner.ergebnis)  # 13

Objekte erstellen und nutzen

class Buch:
    def __init__(self, titel, autor, seiten):
        self.titel = titel
        self.autor = autor
        self.seiten = seiten
        self.aktuelle_seite = 1
        self.lesezeichen = []

    def lesen(self, seiten_anzahl):
        self.aktuelle_seite = min(
            self.aktuelle_seite + seiten_anzahl,
            self.seiten
        )
        print(f"Du bist jetzt auf Seite {self.aktuelle_seite} von {self.seiten}.")

    def lesezeichen_setzen(self):
        if self.aktuelle_seite not in self.lesezeichen:
            self.lesezeichen.append(self.aktuelle_seite)
            print(f"Lesezeichen auf Seite {self.aktuelle_seite} gesetzt.")

    def fortschritt(self):
        prozent = (self.aktuelle_seite / self.seiten) * 100
        return f"{prozent:.1f}%"

# Objekte erstellen
buch1 = Buch("Der Hobbit", "J.R.R. Tolkien", 310)
buch2 = Buch("Python Crashkurs", "Eric Matthes", 544)

# Objekte nutzen
buch1.lesen(50)              # Du bist jetzt auf Seite 51 von 310.
buch1.lesezeichen_setzen()   # Lesezeichen auf Seite 51 gesetzt.
print(buch1.fortschritt())   # 16.5%

# Objekte in Listen speichern
bibliothek = [buch1, buch2]
for buch in bibliothek:
    print(f"'{buch.titel}' von {buch.autor}")

str() und repr()

Diese speziellen Methoden bestimmen, wie ein Objekt als Text dargestellt wird:

class Produkt:
    def __init__(self, name, preis, lagerbestand):
        self.name = name
        self.preis = preis
        self.lagerbestand = lagerbestand

    def __str__(self):
        """Fuer print() und str() - benutzerfreundlich"""
        return f"{self.name} - {self.preis:.2f} EUR"

    def __repr__(self):
        """Fuer Entwickler - eindeutig und reproduzierbar"""
        return f"Produkt('{self.name}', {self.preis}, {self.lagerbestand})"

p = Produkt("Laptop", 999.99, 15)

print(p)        # Laptop - 999.99 EUR        ← nutzt __str__
print(repr(p))  # Produkt('Laptop', 999.99, 15)  ← nutzt __repr__

# In einer Liste wird __repr__ verwendet:
produkte = [p, Produkt("Maus", 29.99, 100)]
print(produkte)  # [Produkt('Laptop', 999.99, 15), Produkt('Maus', 29.99, 100)]

Faustregel:

  • __str__: Fuer den Endbenutzer - huebsch und lesbar
  • __repr__: Fuer den Entwickler - moeglichst eindeutig, idealerweise als gueltiger Python-Code

Praxis: Klasse “Auto”

class Auto:
    """Ein einfaches Auto mit grundlegenden Funktionen."""

    def __init__(self, marke, modell, baujahr, ps):
        self.marke = marke
        self.modell = modell
        self.baujahr = baujahr
        self.ps = ps
        self.geschwindigkeit = 0
        self.km_stand = 0
        self.motor_laeuft = False

    def motor_starten(self):
        if not self.motor_laeuft:
            self.motor_laeuft = True
            print(f"{self.marke} {self.modell}: Motor gestartet! Brumm!")
        else:
            print("Motor laeuft bereits.")

    def motor_stoppen(self):
        if self.motor_laeuft:
            self.geschwindigkeit = 0
            self.motor_laeuft = False
            print(f"{self.marke} {self.modell}: Motor gestoppt.")
        else:
            print("Motor ist bereits aus.")

    def beschleunigen(self, kmh):
        if not self.motor_laeuft:
            print("Erst den Motor starten!")
            return
        self.geschwindigkeit = min(self.geschwindigkeit + kmh, 250)
        print(f"Geschwindigkeit: {self.geschwindigkeit} km/h")

    def bremsen(self, kmh):
        self.geschwindigkeit = max(self.geschwindigkeit - kmh, 0)
        print(f"Geschwindigkeit: {self.geschwindigkeit} km/h")

    def fahren(self, km):
        if not self.motor_laeuft:
            print("Erst den Motor starten!")
            return
        self.km_stand += km
        print(f"{km} km gefahren. Kilometerstand: {self.km_stand} km")

    def __str__(self):
        return f"{self.marke} {self.modell} ({self.baujahr}, {self.ps} PS)"

    def __repr__(self):
        return (f"Auto('{self.marke}', '{self.modell}', "
                f"{self.baujahr}, {self.ps})")

# Testen
mein_auto = Auto("VW", "Golf", 2023, 150)
print(mein_auto)              # VW Golf (2023, 150 PS)
mein_auto.motor_starten()     # VW Golf: Motor gestartet! Brumm!
mein_auto.beschleunigen(80)   # Geschwindigkeit: 80 km/h
mein_auto.fahren(120)         # 120 km gefahren. Kilometerstand: 120 km
mein_auto.bremsen(50)         # Geschwindigkeit: 30 km/h
mein_auto.motor_stoppen()     # VW Golf: Motor gestoppt.

Praxis: Klasse “Bankkonto”

class BankKonto:
    """Ein einfaches Bankkonto mit grundlegenden Funktionen."""

    zinssatz = 0.01  # Klassenattribut: 1% Zinsen fuer alle Konten

    def __init__(self, inhaber, kontonummer, startguthaben=0):
        self.inhaber = inhaber
        self.kontonummer = kontonummer
        self._kontostand = startguthaben  # _ = "privat" (Konvention)
        self._transaktionen = []

    @property
    def kontostand(self):
        """Kontostand als read-only Property."""
        return self._kontostand

    def einzahlen(self, betrag):
        if betrag <= 0:
            print("Betrag muss positiv sein!")
            return False
        self._kontostand += betrag
        self._transaktionen.append(f"+{betrag:.2f} EUR")
        print(f"{betrag:.2f} EUR eingezahlt. Neuer Kontostand: {self._kontostand:.2f} EUR")
        return True

    def abheben(self, betrag):
        if betrag <= 0:
            print("Betrag muss positiv sein!")
            return False
        if betrag > self._kontostand:
            print(f"Nicht genug Guthaben! Verfuegbar: {self._kontostand:.2f} EUR")
            return False
        self._kontostand -= betrag
        self._transaktionen.append(f"-{betrag:.2f} EUR")
        print(f"{betrag:.2f} EUR abgehoben. Neuer Kontostand: {self._kontostand:.2f} EUR")
        return True

    def ueberweisen(self, ziel_konto, betrag):
        if self.abheben(betrag):
            ziel_konto.einzahlen(betrag)
            print(f"Ueberweisung von {betrag:.2f} EUR an {ziel_konto.inhaber} erfolgreich.")

    def zinsen_berechnen(self):
        zinsen = self._kontostand * self.zinssatz
        self._kontostand += zinsen
        self._transaktionen.append(f"+{zinsen:.2f} EUR (Zinsen)")
        print(f"Zinsen: {zinsen:.2f} EUR gutgeschrieben.")

    def kontoauszug(self):
        print(f"\n{'='*40}")
        print(f"Kontoauszug - {self.inhaber}")
        print(f"Kontonummer: {self.kontonummer}")
        print(f"{'='*40}")
        for t in self._transaktionen[-10:]:  # Letzte 10 Transaktionen
            print(f"  {t}")
        print(f"{'='*40}")
        print(f"Aktueller Kontostand: {self._kontostand:.2f} EUR\n")

    def __str__(self):
        return f"Konto {self.kontonummer} ({self.inhaber}): {self._kontostand:.2f} EUR"

# Testen
konto_anna = BankKonto("Anna Mueller", "DE1234567890", 1000)
konto_bob = BankKonto("Bob Schmidt", "DE0987654321", 500)

konto_anna.einzahlen(500)            # 500.00 EUR eingezahlt.
konto_anna.abheben(200)              # 200.00 EUR abgehoben.
konto_anna.ueberweisen(konto_bob, 300)  # Ueberweisung erfolgreich
konto_anna.zinsen_berechnen()        # Zinsen gutgeschrieben

konto_anna.kontoauszug()
print(konto_bob)  # Konto DE0987654321 (Bob Schmidt): 800.00 EUR

Uebungen

Uebung 1: Klasse “Person”

Erstelle eine Klasse Person mit den Attributen name, alter und hobbys (Liste). Fuege Methoden hinzu: vorstellen(), hobby_hinzufuegen(hobby), geburtstag() (erhoeht das Alter um 1) und __str__().

Uebung 2: Klasse “Einkaufswagen”

Erstelle eine Klasse Einkaufswagen mit einer Liste von Produkten (Tupel aus Name und Preis). Implementiere: hinzufuegen(name, preis), entfernen(name), gesamtpreis(), anzahl_artikel() und __str__().

Uebung 3: Klasse “Wuerfel”

Erstelle eine Klasse Wuerfel mit einem Attribut seiten (Standard: 6). Implementiere eine Methode wuerfeln(), die einen zufaelligen Wert zurueckgibt, sowie mehrfach_wuerfeln(anzahl), die eine Liste von Ergebnissen zurueckgibt. Nutze dafuer import random.

Uebung 4: Klasse “Playlist”

Erstelle eine Klasse Playlist mit Name und einer Liste von Songs (jeweils ein Dictionary mit titel, kuenstler, dauer_sekunden). Implementiere: song_hinzufuegen(), song_entfernen(), gesamtdauer() (formatiert als MM:SS), abspielen() (gibt alle Songs aus) und mischen() (zufaellige Reihenfolge).

Pro-Tipp: Wann solltest du eine Klasse erstellen?

Verwende Klassen, wenn du:

  • Mehrere zusammengehoerige Daten hast (Name + Alter + Adresse = Person)
  • Verhalten an Daten knuepfen willst (ein Konto kann einzahlen/abheben)
  • Mehrere Instanzen des gleichen “Typs” brauchst (viele Autos, viele Konten)
  • Zustand verwalten musst (ein Spieler hat Leben, Punkte, Position)

Verwende keine Klassen, wenn eine einfache Funktion oder ein Dictionary ausreicht. In Python gilt: “Einfach ist besser als komplex” (Zen of Python). Nicht alles muss ein Objekt sein!

Zurück zum Python Kurs