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
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).
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 |