6. Klassen

Klassen sind, wie in anderen objektorientierten Sprachen auch, eine Möglichkeit, eigene Typen einzuführen und Objekte davon zu erzeugen. Andererseits ist nicht jeder Typ in einer Klasse festgelegt, wie z.B. die numerischen Basistypen, list und file.

Eine Klasse wird über das Schlüsselwort class mit darauffolgendem Klassennamen erzeugt, danach erwartet der Interpreter einen beliebigen Anweisungsblock, meist Variablendeklarationen und Funktionen.
Im Unterschied zu den meisten anderen klassenbasierten Sprachen sind alle Klassenattribute allgemein zugänglich, es gibt also keinen Schutz vor ungewollter Datenmanipulation von außen. Man kann, da ja auch Funktionen als Objekte gespeichert werden, sogar eine Funktion gegen eine neue Funktion oder eine Variable austauschen.

Eine Instanz wird über Klassenname(...) angelegt. Falls die Klasse eine Methode __init__ besitzt, wird diese ausgeführt und erhält dabei eventuell angegebene Argumente.
Jede Klassenfunktion hat zumindest einen Parameter, der beim Aufruf die aktuelle Empfängerinstanz angibt. Das heißt, wenn x Instanz von X ist, dann ist x.f() äquivalent zu X.f(x).

Beispiel:
>>> class SimpleClass:
...    i = 15    # Attribut
...    def printHello(self): # Funktion, erwartet als
                             # Parameter das Empfängerobjekt
                             # d.h. die Instanz der Klasse
...        print "Hello"
...
>>> sc = SimpleClass()       # neue Instanz anlegen
>>> sc.i
15
>>> sc.printHello()          # äquivalent zum nächsten Befehl
Hello
>>>SimpleClass.printHello(sc)
Hello
>>> sc.i = 16                # Zuweisung auf Feld der Instanz
>>> sc.i
16
>>> SimpleClass.i
15
>>> SimpleClass.i = 20
>>> SimpleClass.i
20
>>> sc.i
16
>>> def printGoodbye(x):     # Hilfsfunktion
...     print "Goodbye"
...
>>> SimpleClass.printHello = printGoodbye
# Hier wird einfach die Funktion überschrieben!
# Das wirkt sich auch auf die Instanz sc aus
>>> SimpleClass.printHello(sc)
Goodbye
>>> sc.printHello()
Goodbye
>>> SimpleClass.printHello = 1
# Noch radikaler - aus der Funktion wird ein Integer!
>>> sc.printHello
1
>>> SimpleClass.printHello
1
 

Vererbung

Python wäre keine objektorientierte Sprache, wenn es keine Vererbung unterstützen würde. Tatsächlich ist sogar Mehrfachvererbung problemlos möglich.
Gewöhnliche Vererbung sieht so aus:
class Sub(Base1, Base2, .. , BaseN):
...
Danach gibt es die neue Klasse namens Sub, die von den Klassen Base1 bis BaseN erbt. Auf geerbte Felder kann mittels Supercall zugegriffen werden.

Die Syntax dafür lautet:
BaseClassName.attrname bei Attributen
bzw. BaseClassName.funcName(self, args) bei Funktionen
Methoden können bei gleicher Schnittstelle natürlich auch überschrieben werden. Die Methodenbindung erfolgt dynamisch, die Attributbindung statisch (wie in Java).

Attribut-/Methodensuche

Bei nur einer Subklasse ist die Sache einfach:
Erhält einen Instanz eine nicht verstandene Anfrage, wird sie an die Superklasse weitergeleitet, die sie wieder weitergibt, bis das Feld entweder gefunden wird oder keine Superklasse mehr existiert.
Bei mehreren Superklassen wird nach zwei Regeln verfahren: Das bedeutet, daß jede Klasse zuerst bei ihrer links stehenden Oberklasse anfragt, bis keine mehr da ist. Dann leitet die am weitesten links oben in der Hierarchie stehende Klasse mit noch unbefragten Superklassen die Anfrage an ihre nächstlinke Oberklasse weiter usw., bis das Attribut gefunden wird oder alle Superklassen durchsucht wurden.
Diese Vorgehensweise macht es möglich, beim Ableiten die Superklassen zu 'priorisieren' und verhindert zugleich durch die mehrfache Vererbung auftretende Konflikte.

Beispiel:
class Building:
    size

class House(Building):
    ...

class Vehicle:
    ...

class Boat(Vehicle):
    size

class HouseBoat(House, Vehicle):
    ...

Die Klasse HouseBoat erbt im Beispiel sowohl von House als auch von Boat, die ihrerseits wieder von allgemeineren Klassen abstammen.
Wird nun das Attribut size eines solchen Hausboots zugegriffen, zeigt sich, warum hier die Breiten- und nicht die Tiefensuche gewählt wurde:
Ergebnis bei Breitensuche:
Suche size in HouseBoat. Nicht gefunden.
Suche size in House (links vor rechts). Nicht gefunden.
Suche size in Boat (Breitensuche). Gefunden!
Natürlich gibt es size in House sehr wohl, allerdings nur ererbt. Die Priorisierung (hauptsächlich erbe ich von House, aber auch von Boat) ist hier also weggefallen.

Ergebnis bei Tiefensuche:
Suche size in HouseBoat. Nicht gefunden.
Suche size in House (links vor rechts). Nicht gefunden.
Suche size in Building (Tiefensuche). Gefunden!
Damit habe ich als Programmierer eine verläßliche Methode, vorherzusagen, welches size im Konfliktfall herangezogen werden wird und muß nicht nachsehen, auf welcher Stufe es geerbt wurde.
 
 

Kontrollstrukturen