6. Klassen

Python unterstützt auch die objektorientierte Programmierung. Es können Klassen definiert werden, die Attribute und Methoden kapseln. Durch die gemeinsame Definition von Attributen und Methoden innerhalb des Namensraums einer Klasse wird Modularisierung und Zugriffskontrolle erreicht. Da die Klassenarchitektur möglichst an die reale Welt angepasst sein sollte (d.h. was in der realen Welt zusammengehört, sollte auch innerhalb einer Klasse modelliert werden) wird die Verständlichkeit des Codes gegenüber anderen Programmieransätzen wie z.B. der prozeduralen Programmierung deutlich verbessert. Wie in anderen Programmiersprachen, definieren auch in Python Klassen nur einen abstrakten Bauplan. Konkrete Realisierungen von Klassen werden Objekte, oder Instanzen genannt. Da von einer Klasse beliebig viele Instanzen erzeugt werden können, wird mit der Objektorientierung auch der Vorteil des Code-Reuse erreicht. Jedes Objekt einer Klasse bildet einen eigenen Namensraum (abgesehen von den statischen Attributen). Attribute und Methoden können von aussen zugänglich (public) oder nicht zugänglich (privat) sein. Python unterstützt auch das Konzept der Vererbung, welches sowohl Code-Reuse als auch Verständlichkeit fördert. Im Gegensatz zu Java ist in Python Mehrfachvererbung möglich. Ein weiterer Unterschied zu Java ist, dass in Python alles Objekt ist, d.h. es gibt keine primitve Typen, selbst ganze Zahlen, oder boolsche Variablen sind in Python Objekte.

6.1. Definition von Klassen

In Python wird eine Klasse innerhalb eines Files definiert. Dieses File kann auch mehrere Klassen oder ausserhalb von Klassen definierte Variablen und Funktionen enthalten. Derartige Files werden in Python Module genannt und müssen die Extension .py aufweisen.

Die Definition einer Klasse beginnt in Python mit einer Kopfzeile der Form:

class meineKlasse:

Dem Schlüsselwort class folgt der Name der Klasse. Nach dem Klassennamen folgt im Fall, dass die Klasse von keiner anderen Klasse abgeleitet wird der Doppelpunkt. Alle folgenden Anweisungen, die den Rumpf der Klassendefinition bilden, müssen wie in Python üblich eingerückt werden. Die Definition einer Klasse kann jedoch nicht nur aus der Kopfzeile bestehen (Syntaxfehler). Während des Entwicklungsprozess kann es vorkommen, dass man zunächst nur den Kopf einer Klasse definieren möchte, die Definition des Klassenrumpfs jedoch erst später schreiben möchte. Um in diesem Fall einen Syntaxfehler zu vermeiden, ist in Python das Schlüsselwort pass vorgesehen. Dieses Schlüsselwort kann überall dort eingefügt werden wo vorübergehend noch kein Anweisungsblock geschrieben werden soll, ein solcher aber für die syntaktische Korrektheit notwendig wäre.

Folgender Programmcode erzeugt also keinen Syntaxfehler:

class meineKlasse:
        pass

Das Schlüsselwort pass kann auch als vorübergehender Rumpf einer Funktionsdefinition oder als vorübergehender Rumpf einer Kontrollstruktur (z.B. if-then-else Anweisung) benutzt werden.

6.1.1. Methoden

Methoden sind Funktionen, die innerhalb einer Klasse definiert werden. Sie sind innerhalb der Klasse sichtbar und benutzbar. Von aussen kann auf Methoden nur über eine Referenz auf ein Objekt dieser Klasse zugegriffen werden und dies auch nur dann, wenn die Methode nicht als privat deklariert wird. Eine weitere Besonderheit von Methoden ist, dass das erste Element innerhalb der Parameterliste immer self sein muss. Über den Parameter self erhält die Methode beim Aufruf eine Referenz auf das Objekt für welches sie aufgerufen wird. Folgender Code definiert 2 Methoden innerhalb einer Klasse. Der Rumpf der Methoden wird vorerst noch nicht definiert:

class meineKlasse:
        def methode1(self,par1,par2):
                pass
        def methode2(self,par1='a',par2=3):
                pass

6.1.2. Attribute

Wie in anderen Programmiersprachen wird auch in Python zwischen Instanzattributen und Klassenattributen (statische Attribute) unterschieden, wobei letztere eher in Ausnahmefällen verwendet werden. Wie der Name schon sagt gehören Instanzattribute zu den individuellen Instanzen einer Klasse. D.h. werden zwei Objekte derselben Klasse instanziert, dann wird für beide beim Erzeugen ein jeweils eigener Pool von Instanzattributen angelegt. Der Wert des Instanzattributs att in Objekt 1 ist völlig unabhängig vom Wert des Instanzattributs att in Objekt 2 derselben Klasse. Hingegen wird ein Klassenattribut pro Klasse nur einmal angelegt. D.h. alle Objekte einer Klasse können gemeinsam auf das eine Klassenattribut zugreifen. Häufig werden Klassenattribute verwendet um die aktuell instanzierten Objekte einer Klasse zu zählen.

Klassenattribute werden in Python direkt im class-Block unter Zuweisung eines Initialwerts definiert (d.h. nicht innerhalb des Konstruktors oder einer anderen Methode). Instanzattribute hingegen werden im Konstruktor definiert und mit Initialwerten versehen.

6.1.3. Sichtbarkeit

Wie oben bereits erwähnt werden innerhalb von Klassen Attribute und Methoden definiert. Die Sichtbarkeit von Methoden und Attributen ausserhalb der Klasse in der sie definiert sind kann vom Programmierer eingestellt werden. Ein wesentliches Prinzip der objektorientierten Programmierung ist es, Attribute nur über Methoden von aussen zugänglich zu machen, d.h. auf Attribute sollte aus Gründen der Sicherheit und Zugriffskontrolle von ausserhalb nicht direkt zugegriffen werden, sondern nur über Methoden, die u.a. die Zulässigkeit des Zugriffs kontrollieren. Python unterstützt folgende 3 Sichtbarkeitsstufen:

  • public Attribute und Methoden sind von ausserhalb direkt über eine Referenz auf das Objekt der Klasse zugänglich. Beispiel: ref.att und ref.meth() definieren den Zugriff auf ein public Attribut att und eine public Methode meth(), die in einer Klasse definiert sind, von deren Typ das Objekt ist, auf welches ref zeigt. Default, also ohne spezielle Kennzeichnung, sind in Python alle Attribute und Methoden public.
  • private Attribute und Methoden sind von aussen nicht sichtbar. Attribute und Methoden werden als private deklariert, indem man ihren Namen mit dem Prefix __ (doppelter Unterstrich) versieht.
  • protected Attribute und Methoden werden gekennzeichnet, indem man dem Namen einen einfachen Unterstrich _ voranstellt. Technisch ist für diese Attribute und Methoden der Zugriff von aussen genauso möglich wie im Fall public. Wird ein Attribut oder eine Methode als protected definiert, so soll das lediglich andere Programmierer darauf hinweisen, dass sie keinen direkten Zugriff von aussen implementieren sollen. Es handelt sich hierbei also nur um eine Konvention oder Empfehlung.

6.1.4. Konstruktor

Beim Erzeugen eines Objekts wird für dieses ein eigener Satz von Instanzattributen angelegt. Diese Erzeugung und Initialisierung wird in einer speziellen Methoden durchgeführt, dem Konstruktor. Der Konstruktor wird in Python durch die Kopfzeile:

def __init__(self):

definiert. Es handelt sich hierbei um eine spezielle private Methode, die von aussen nicht direkt aufgerufen werden kann, sondern nur automatisch beim Erzeugen eines neuen Objekts aufgerufen wird. Wie alle anderen Methoden erhält auch der Konstruktor als ersten Parameter (self) eine Referenz auf das zugehörige Objekt. Im Rumpf des Konstruktors müssen alle Instanzattribute der Klasse angelegt werden. Da diese Attribute zum gerade erzeugten Objekt gehören und dieses mit self referenziert wird, ist den Namen der Instanzattribute jeweils self. voranzustellen. Der Konstruktor einer Klasse mit einem Instanzattribut instatt könnte wie folgt aussehen:

def __init__(self):
    self.instatt="start"
    print self.instatt

Um die Instanzattribute von Objekten beim Erzeugen individuell einzustellen, muss der Konstruktor diese Initialwerte aufnehmen können. Die Parameterliste des Konstruktors muss also entsprechend erweitert werden:

def __init__(self,inittext):
    self.instatt=inittext
    print self.instatt

Für die Parameterübergabe an Konstruktoren und Methoden gelten die gleichen Freiheiten wie im Fall der Funktionen, d.h. durch Vorbelegung der Parameter mit Standardwerten muss beim Aufruf nicht zwingend für alle Parameter ein Argument übergeben werden.

6.1.5. Destruktor

Ist objref eine Referenz auf ein Objekt, dann kann das Objekt durch:

del(objref)

gelöscht werden. Sollen bei einem derartigen Löschen von Objekten bestimmte Aktionen durchgeführt werden, so kann für die entsprechende Klasse ein Destruktor definiert werden. Der Destruktor ist eine spezielle Methode, die automatisch beim Löschen eines Objekts mit del() aufgerufen wird. Der Destruktor muss mit der Kopfzeile:

def __del__()

definiert werden. In den Rumpf des Konstruktors werden die beim Löschen des Objekts durchzuführenden Anweisungen geschrieben.

6.2. Erzeugen von Objekten

Objekte einer Klasse meineKlasse können durch den Aufruf:

ref=meineKlasse(arg1,arg2,...)

erzeugt werden. Die innerhalb der geschweiften Klammern angeführten Argumente werden dem Konstruktor übergeben. Enthält der Konstruktor als Parameter nur self, dann können die geschweiften Klammern nach dem Klassennamen ganz weggelassen werden.

6.3. Beispiel: Definition von Klassen/Objekte erzeugen und benutzen

Folgende Klasse kapselt die Daten und Funktionen für eine einfache Kontoverwaltung:

class Konto:
        angelegteKonten=0

        def __init__(self,inhaber,autorisiert=["Bankangestellter"],startkap=0):
                self.__inhaber=inhaber
                self.__autorisiert=autorisiert
                self.__kontostand=startkap
                Konto.angelegteKonten+=1

        def __del__(self):
                Konto.angelegteKonten-=1

        def einzahlen(self,betrag):
                if (type(betrag)==float or type(betrag)==int or type(betrag)==long) and betrag>0:
                        self.__kontostand +=betrag
                        print "Neuer Kontostand:    ",self.__kontostand
                else:
                        print "FEHLER: Falsche Betragsangabe"
                return self.__kontostand

        def auszahlen(self,betrag,initiator):
                if not initiator in self.__autorisiert:
                        print initiator+" ist nicht berechtigt"
                elif self.__kontostand < betrag:
                        print "Es befinden sich nur noch %10.2f Euro auf dem Konto" % self.__kontostand
                else:
                        self.__kontostand -=betrag
                return self.__kontostand

        def abfrage(self):
                return self.__kontostand

Die Klasse enthält drei Instanzattribute, die im Konstruktor angelegt werden. Ausserdem ist ein Klassenattribut (angelegteKonten) definiert mit dem die Anzahl der angelegten Konten gezählt werden soll. Hierfür wird bei jedem Erzeugen eines neuen Objekts der Klasse die Zählvariable inkrementiert und bei jedem Löschen eines Objekts im Destruktor dekrementiert. Sämtliche Attribute sind als private deklariert, d.h. man kann auf sie nur über die als public definierten Methoden zugreifen.

Der folgende Programmcode zeigt wie Objekte der Klasse Konto angelegt und benutzt werden können:

import klassenbeispiele1 as classbsp

kontoSchwarz=classbsp.Konto("Schwarz",["Schwarz","Bankangestellter","Papa"],10)
print kontoSchwarz.einzahlen(1499.00)
print kontoSchwarz.auszahlen(1000, "Freundin")
print kontoSchwarz.abfrage()
print kontoSchwarz.auszahlen(1600, "Papa")
print kontoSchwarz.auszahlen(900, "Schwarz")

print "Anzahl der Konten  :",classbsp.Konto.angelegteKonten

kontoWeiss=classbsp.Konto("Weiss")

print "Anzahl der Konten  :",classbsp.Konto.angelegteKonten

print "Kontostand von Schwarz :",kontoSchwarz.abfrage()
print "Kontostand von Weiss :",kontoWeiss.abfrage()

del(kontoWeiss)

print "Anzahl der Konten  :",classbsp.Konto.angelegteKonten

Das Programm erzeugt folgende Ausgabe:

Neuer Kontostand:     1509.0
1509.0
Freundin ist nicht berechtigt
1509.0
1509.0
Es befinden sich nur noch    1509.00 Euro auf dem Konto
1509.0
609.0
Anzahl der Konten  : 1
Anzahl der Konten  : 2
Kontostand von Schwarz : 609.0
Kontostand von Weiss : 0
Anzahl der Konten  : 1

6.4. Vererbung

Soll eine Klasse meineKlasse von den Klassen C1, C2, usw. abgeleitet werden, dann muss die Klassendefinition mit der Kopfzeile:

class meineKlasse(C1,C2,...):

beginnen. Alle Attribute und Methoden der Elternklassen C1, C2,... sind dann auch in meineKlasse verfügbar und können dort ggf. überschrieben werden.