Eigene Klassen

Häufig ist es sinnvoll eigene Klassen/Datentypen zu erstellen, bei Programmen mit einer GUI ist es fast unerlässlich.

Definition

Die Klasse ist also der Bauplan, aus dem ‚konkrete‘ Objekte (Instanzen) erstellt werden. Klassennamen beginnen vereinbarungsgemäß mit einem Groß-Buchstaben !

Wir beginnen mit der leeren Hülle einer Klasse: (die Anweisung pass „macht nichts“, es geht nur um die Syntax/Sprachregeln, da ja nach dem ‚:‘ etwas Eingerücktes stehen muss.)

class Tier():
    pass

# Der leere Bauplan genügt schon, um daraus Objekte erzeugen zu können.
elch = Tier()

Methoden und Instanzvariablen

Jetzt wollen wir die Klasse mit etwas Inhalt füllen und erstellen eine Methode. Eine Methode ist eine spezielle Art von Funktion, die nur zusammen mit einem Objekt dieser Klasse verwendet werden kann.

Methoden haben in ihrer Parameterliste immer als erstes ein self stehen. Beim Aufruf fehlt dieser Parameter, d.h. der Aufruf hat immer ein Argument weniger als es Parameter in der Definition der Methode gibt.

class Tier():
    def sprich(self):
        print("Hallo")

elch = Tier()
elch.sprich()

Die Dinge, die bei jedem einzelnen Objekt einen eigenen Wert haben können, werden in Attributen, oder besser Instanzvariablen gespeichert.

Zu erkennen sind Instanz-Variablen daran, dass sie mit einem self beginnen. Sie haben den großen Vorteil, dass sie in allen anderen Methoden derselben Klasse einfach weiterverwendet werden können.

class Tier():
    def setfutter(self, futter):
        # eine Instanzvariable definieren
        self.futter = futter

    def sprich(self):
        # die Instanzvariable kann hier verwendet werden !!!
        print("Ich mag", self.futter)

elch = Tier()
elch.setfutter("Kräuter")
elch.sprich()

Konstruktor

Allerdings gibt es bei diesem Programm noch ein Problem, denn was passiert, wenn wir die Methode sprich() verwenden, bevor wir die Methode setfutter() aufgerufen haben?

Es gibt einen Fehler, denn die Instanzvariable self.futter wurde noch nicht definiert, der entsprechende Programmcode wurde noch nicht durchlaufen.

Dass läßt sich aber leicht vermeiden, denn wie bei allen objektorientierten Sprachen, gibt es die Möglichkeit ein Objekt zu initialisieren. Ein solche Methode wird (in allen Objekt-orientierten-Sprachen) als Konstruktor bezeichnet.

Wenn also in einer Klasse eine Methode mit dem dem festgelegten Namen __init__(...) definiert wurde (jeweils zwei Unterstriche), dann wird diese Methode einmal beim Erstellen eines jeden Objekts ausgeführt. Das ist der richtige Ort, um den Instanzvariablen einen Anfangswert zu geben, sie also zu initialisieren. Ich würde also grundsätzlich empfehlen, alle Instanzvariablen schon im Konstruktor anzulegen.

class Tier():
    def __init__(self):
        print("Konstruktor Tier")
        # Instanzvariablen initialisieren
        self.futter = "alles"
        self.gewicht = None

    def
        ...

elch = Tier()
elch.sprich()

Das Schlüsselwort None bedeutet, dass der Wert noch nicht festgelegt ist. Die Instanzvariable self.gewicht ist damit definiert und kann verwendet werden ohne dass das Programm abstürzt.

Der Konstruktor kann auch Parameter haben und damit lässt sich sicherstellen, dass z.B. für jedes Tier eine Tierart festgelegt werden muss (=mandatory).

class Tier():
    def __init__(self, art):
        print("Konstruktor Tier:", art)
        self.art = art
        self.futter = "alles"

    def sprich(self):
        print("Ich bin ein ", self.art, "und mag gerne", self.futter)


tier = Tier("Elch")
tier.sprich()

Namensraum

Setter- und Getter-Methoden sind die klassische Art um den Wert von Instanzvariablen zu setzen oder zu ermitteln. Natürlich könnten die Methodennamen auch anders heißen, aber set…() und get…() haben sich eingebürgert.

class Tier():
    def __init__(self):
        self.futter = "alles"

    def setfutter(self, futter):
        self.futter = futter

    def getfutter(self):
        return self.futter


elch = Tier()
elch.setfutter("Kräuter")
myfood = elch.getfutter()
print("und mag", myfood)

In vielen Objektorientierten Sprachen, kann auf einzelne Objekte nur über Methoden zugegriffen werden, in Python ist es aber auch direkt über den Objektnamen möglich. Das Objekt bildet dabei sozusagen einen eigenen Namensraum.

In diesem Beispiel könnte ich das Futter also auch direkt verwenden:

elch = Tier()
elch.setfutter("Kräuter")
print("Ich mag", elch.futter)

Methoden

Soll innerhalb einer Methode eine andere Methode der gleichen Klasse aufgerufen werden, geschieht das in der üblichen Weise, links vom Punkt das Objekt und rechts die Methode:

class Tier():
    def __init__(self, art):
        self.art = art
        self.futter = "alles"

    def sprich(self):
        print("Ich bin ein ", self.art, "und mag gerne", self.futter)

    def setfutter(self,futter):
        self.futter = futter
        # ==>  Um eine Methode aus der eigenen Klasse aufzurufen,
        # wird vor den Namen der Methode das 'self' gestellt !
        self.sprich()

tier = Tier("Elch")
tier.sprich()