Klassen und Objekte

Einführung

Die grundlegende Philosophie des objektorientierten Programmierens ist, Objekte der realen Welt anhand ihrer Eigenschaften und Fähigkeiten zu modellieren. Gleichartige Objekte, d.h. Objekte, die sich durch dieselben Eigenschaften und Fähigkeiten beschreiben lassen, fasst man dabei zu sogenannten Klassen zusammen. Bevor wir aber viel theoretisieren, schauen wir uns ein Beispiel an:

Ein erstes Klassendiagramm. Ein paar Details werden später noch ergänzt.

Dies ist ein sogenanntes Klassendiagramm. Einige Details fehlen in diesem Diagramm noch, da diese erst später für uns Sinn ergeben. Der Aufbau eines solchen Diagramms ist wie folgt:

  • Im ersten Feld steht (in Fettdruck) der Name der Klasse.
  • In dem zweiten Feld stehen die Eigenschaften, die uns an Objekten dieser Klasse interessieren. Diese Eigenschaften nennt man die Attribute dieser Klasse. Man gibt dort an, von welchem Typ diese Attribute sind.
  • Im dritten Feld gibt man Fähigkeiten von Objekten dieser Klasse an. Diese nennt man die Methoden der Klasse. Anders ausgedrückt kann man sich auch vorstellen, dass dies die möglichen Anweisungen sind, auf die ein Objekt dieser Klasse sinnvoll reagieren kann. Man gibt dort auch an, ob diese Methoden einen oder mehrere Parameter benötigen und von welchem Typ diese sein müssen. Sollten sie einen Rückgabewert liefern, so wird auch sein Typ angegeben. Die Methoden der Klasse Auto, die oben angegeben sind, liefern keinen Rückgabewert. Zu Methoden mit Rückgabewert kommen wir noch später.

Sehen wir uns einmal an, wie wir diese Klasse in Python umsetzen könnten:

# Datei Auto.py
class Auto:

    def __init__(self, baujahr, marke, kilometerstand, automatik, preis):
        self.baujahr = baujahr
        self.marke = marke
        self.kilometerstand = kilometerstand
        self.automatik = automatik
        self.preis = preis

    def mache_probefahrt(self, kilometer):
        self.kilometerstand = self.kilometerstand + kilometer

    def aendere_preis_um(self, aenderung):
        if self.preis+aenderung > 0:
            self.preis = self.preis + aenderung
        else:
            print("Die Änderung ist nicht zulässig!")

Beachte, dass man üblicherweise die Namen von Attributen und Methoden in Python klein schreibt. Außerdem sollten wir keine Umlaute verwenden, da diese Schwierigkeiten verursachen können.

Vielleicht fällt Dir auf, dass beim Starten dieser Datei nichts passiert. Dies ist aber typisch für das objektorientierte Programmieren: Man stellt zunächst einige Klassen bereit und dann schreibt man ein Programm, das die vorher vorbereiteten Klassen verwendet.

Hier ein kleines Beispielprogramm, das die Klasse Auto verwendet:

# Datei Auto_Test.py
from Auto  import *

mein_erstes_auto = Auto(1985, "DeLorean", 54000, False, 60000)

print("Erstes Auto:")
print(mein_erstes_auto.baujahr)
print(mein_erstes_auto.marke)
print(mein_erstes_auto.kilometerstand)
print(mein_erstes_auto.automatik)
print(mein_erstes_auto.preis)

mein_erstes_auto.mache_probefahrt(50)
print(mein_erstes_auto.kilometerstand)

Um es noch einmal zu betonen: Wir verwenden nun zwei Dateien, nämlich Auto.py und Auto_Test.py. Nur Auto_Test.py kann (in sinnvoller Weise) gestartet werden.

Sehen wir uns die Anweisungen von in Auto_Test.py noch einmal im Detail an.

Zunächst wird eine Variable mein_erstes_auto angelegt. Sie verweist auf den Rückgabewert von Auto(1985, "DeLorean", 54000, False, 60000). An dieser Stelle wird die init-Methode von Auto aufgerufen. Dies ist eine besondere Methode für Klassen in Python. Sie dient dem Erzeugen eines Objekts und dem Zuweisen von Werten der Attribute. Diese sehen wir uns weiter unten noch einmal genauer an.

Nachdem unser Auto erschaffen wurde, geben wir die gespeicherten Attributwerte aus. Das Schema ist dabei immer gleich; zuerst kommt der Name der Variablen, unter der das Objekt abgelegt wurde, dann ein Punkt gefolgt vom Namen des Attributs. Diese Schreibweise ist typisch in der objektorientierten Programmierung.

Fast genauso können wir auch auf die Methoden des Objekts zugreifen. Bei den Methoden ist jedoch zu beachten, dass bei diesen am Ende immer Runde Klammern stehen müssen. Wenn die Methode Parameter verlangt, kommen Werte für die Parameter in die Klammern. Aber selbst wenn eine Methode keinen Parameter verlangt, müssen wir die runden Klammern schreiben. Diese sind dann aber leer. Ein Beispiel dazu werden wir weiter unten sehen.

Wir rufen hier zur Verdeutlichung die Methode mache_probefahrt(self, kilometer) auf. Diese Methode benötigt zwei Parameter. Der zweite leuchtet ein: Er gibt an, wie lang die Probefahrt ist. Was aber bedeutet der erste?

Um dies zu verstehen, muss man wissen, dass beim Aufruf einer Methode eines Objekts in Python immer automatisch als erster Parameterwert eine Referenz auf das ausführende Objekt übergeben wird, also auf das Objekt, für das wir die Methode aufrufen. Da Python dies im Hintergrund macht, müssen wir für diesen Parameter keinen Wert angeben, sondern nur für die übrigen. In diesem Fall geben wir also nur die Anzahl der Kilometer an.

Innerhalb der Methode können wir nun mit dem Parameter self auf das aktuelle Objekt und dessen Attribute zugreifen. Das bedeutet, self.kilometerstand ist der Kilometerstand von mein_erstes_auto. Innerhalb der Methode mache_probefahrt(self, kilometer) würde es keinen Sinn ergeben, über mein_erstes_auto.kilometerstand auf diesen zugreifen zu wollen, da die Bezeichnung mein_erstes_auto erst in Auto_Test.py eingeführt wird.

Zuletzt geben wir erneut den Kilometerstand aus, der nun tatsächlich erhöht wurde.

Konstruktor

In unserem Beispiel von oben haben wir bereits von der init-Methode der Klasse Auto Gebrauch gemacht. Diese Methode wird in der Literatur oft auch als Konstruktormethode oder kurz Konstruktor bezeichnet.


Anmerkung: Manchmal wird auch zwischen dieser Methode und dem Konstruktor, der für uns unsichtbar von Python automatisch gestartet wird, unterschieden. Da der Zweck dieser Methode aber sehr an den einen Konstruktors wie z.B. in Java erinnert, verwenden wir hier ebenfalls die Bezeichnungen Konstruktormethode oder Konstruktor.


Einen Konstruktor kann man sich als eine Bauanleitung vorstellen: Er gibt an, welche Daten man zur Erschaffung eines Objektes dieser Klasse benötigt. In unserem Beispiel sind es das Baujahr, die Marke, der Kilometerstand, ob das Auto Automatik hat und der Preis, wie man an den Parametern des Konstruktors erkennt. So wie die anderen Methoden einer Klasse sehen wir wieder den zusätzlichen Parameter self. Diesen nutzen wir hier aus, um die übergebenen Werte den Attributen zuzuordnen.

Ausgabe als String

Neben der Konstruktormethode sehen wir uns als weitere besondere Methode die str-Methode an. Diese dient dazu, ein Objekt automatisch in einen String umwandeln zu lassen. Auf diese Weise kann man Objekte sehr einfach anzeigen lassen:

# Datei Auto.py
class Auto:

    def __init__(self, baujahr, marke, kilometerstand, automatik, preis):
        self.baujahr = baujahr
        self.marke = marke
        self.kilometerstand = kilometerstand
        self.automatik = automatik
        self.preis = preis

    def mache_probefahrt(self, kilometer):
        self.kilometerstand = self.kilometerstand + kilometer

    def aendere_preis_um(self, aenderung):
        if self.preis+aenderung > 0:
            self.preis = self.preis + aenderung
        else:
            print("Die Änderung ist nicht zulässig!")

    def __str__(self):
        return str(self.baujahr) + " " + str(self.kilometerstand)

Hier wird in dieser Methode ein String erzeugt und zurückgegeben, der uns das Baujahr und den Kilometerstand verrät. Wir könnten die Ausgabe noch beliebig anpassen, aber hier soll diese kurze Version erst einmal genügen.

So sieht dann ein Einsatz dieser Methode aus:

# Datei Auto_Test.py
from Auto  import *

mein_erstes_auto = Auto(1985, "DeLorean", 54000, False, 60000)

print(mein_erstes_auto) # Ausgabe: 1985 54000

Ohne diese Methode hätten wir die wenig informative Ausgabe <Auto.Auto object at 0x7f7409919278> (evtl. mit anderen Zahlenwerten) erhalten.