Klassen in Python - Grundlagen
Lerne die Grundlagen der objektorientierten Programmierung in Python: Klassen, Objekte, Konstruktoren, Attribute und Methoden.
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!