Mit dem Begriff Bindung bezeichnet man die Auswahl der Implementierung einer Methode zu einem gegebenen Namen. Da Unterklassen Methoden überschreiben können, ist nicht immer ohne weiteres klar, welche Implementierung anzuwenden ist, wie die folgende Diskussion zeigt.
Um die Bindung von Methoden zu verdeutlichen, definieren wir
für jede Unterklasse von Shape eine Methode __repr__ zur Darstellung der
entsprechenden Figur als Zeichenkette.
Da jede Klasse eine __repr__-Methode der Klasse object erbt,
überschreiben wir die geerbte Implementierung mit einer eigenen.
Wir beginnen mit einer __repr__-Methode für die Shape-Klasse.
# in class Shape
def __repr__(self):
return "Shape location=" + str(self._location)
Damit die im Attribut _location gespeicherte Point-Instanz
in eine sinnvolle Zeichenkette umgewandelt wird, überschreiben wir
die __str__-Methode in der Point-Klasse.
# in class Point
def __str__(self):
return "(" + str(self._x) + "," + str(self._y) + ")"
Die drei folgenden Anweisungen erzeugen einen Kreis und ein Rechteck. Anschließend können wir uns in der interaktiven Python-Umgebung deren Darstellung anzeigen lassen.
>>> p = Point(100, 100)
>>> c = Circle(p, 50)
>>> r = Rect(p, 150, 100)
>>> c
Shape location=(100,100)
>>> r
Shape location=(100,100)
Wenn die Rect-Klasse Zugriff auf die linke obere Ecke mit top_left
sowie auf Breite und Höhe mit
Methoden width und height erlaubt, können wir die Methode __repr__
in der Klasse Rect wie folgt überschreiben.
# in class Rect
def __repr__(self):
return (
"Rect top_left=" + str(self.top_left()) +
" width=" + str(self.width()) +
" height=" + str(self.height())
)
Analog dazu können wir die Methode __repr__ in Circle definieren,
indem wir die gespeicherten Attributwerte zusammenfassen.
# in class Circle
def __repr__(self):
return (
"Circle center=" + str(self.center()) +
" radius=" + str(self.radius())
)
Wenn wir nun die oben gemachten Eingaben wiederholen, werden diese
beiden unterschiedlichen Implementierungen der __repr__-Methode
aufgerufen, je nachdem, auf welchem Objekt __repr__ aufgerufen
wird. Dies geschieht selbst dann, wenn wir die Aufrufe in einer
Schleife zusammenfassen.
>>> p = Point(100, 100)
>>> c = Circle(p, 50)
>>> r = Rect(p, 150, 100)
>>> shapes = [c, r]
>>> for i in range(0, len(shapes)):
... print(repr(shapes[i]))
...
Circle center=(100,100) radius=50
Rect top_left=(100,100) width=150 height=100
Obwohl hier textuell nur ein einziger repr-Aufruf steht, werden in
unterschiedlichen Schleifendurchläufen unterschiedliche
Implementierungen der __repr__-Methode verwendet, je nachdem zu welcher Klasse das Element shapes[i] aus der durchlaufenen Liste gehört. Da hier erst zur Laufzeit feststeht, welche Implementierung verwendet wird, spricht man von dynamischer Bindung.
Eine Variante der dynamischen Bindung ist die sogenannte späte Bindung, die wir auch mit Hilfe der __repr__-Methode illustrieren. Dazu fügen wir der Klasse Shape eine Methode __str__ hinzu, die das erste von __repr__ gelieferte Wort zurück liefert.
# in class Shape
def __str__(self):
return repr(self).split()[0]
Der Aufruf von repr verwendet hier die
Implementierung derjenigen Unterklasse von Shape, auf der
__str__ aufgerufen wurde, selbst wenn diese bei der Definition
von __str__ gar nicht bekannt ist.
Da die Rect-Klasse die in Shape definierte Methode __str__
erbt, können wir sie auf dem Rechteck r aufrufen um seine Beschreibung
zu generieren.
>>> p = Point(100, 100)
>>> r = Rect(p, 150, 100)
>>> print(r)
Rect
Ein Aufruf von print ruft indirekt __str__ auf.