Informatik I: Einführung in die Programmierung
14. Objekt-orientierte Programmierung: Aggregierung, Properties, Invarianten, Da- tenkapselung
Albert-Ludwigs-Universität Freiburg
Prof. Dr. Peter Thiemann
21.12.2021
Aggregie- rung
Properties Operator- Überladung Zusammen- fassung
1 Aggregierung
21.12.2021 P. Thiemann – Info I 3 / 41
Aggregie- rung
Properties Operator- Überladung Zusammen- fassung
Zusammengesetzte Objekte
Oft sind Objekte aus anderen Objektenzusammengesetzt.
Methodenaufrufe auf ein zusammengesetztes Objekt führen meist zu Methodenaufrufen auf eingebetteten Objekten.
Beispiel: ein zusammengesetztes 2D-Objekt, das andere 2D-Objekte enthält, z.B. einen Kreis und ein Rechteck.
Aggregie- rung
Properties Operator- Überladung Zusammen- fassung
Die Klasse Composite (1)
Jede Instanz ist ein2D-Objekt, aber eine Position macht keinen Sinn.
Zusätzlich hat jede Instanz als Attribut eineListevon 2D-Objekten.
newgeoclasses.py (1)
@ d a t a c l a s s
c l a s s C o m p o s i t e ( T w o D O b j e c t ):
c o n t e n t s : l i s t[ T w o D O b j e c t ] = f i e l d ( i n i t = F a l s e ) d e f _ _ p o s t _ i n i t _ _ ( s e l f ):
t h i s . c o n t e n t s = []
d e f add ( self , * o b j s : l i s t[ T w o D O b j e c t ]):
s e l f . c o n t e n t s . e x t e n d ( o b j s ) d e f rem ( self , obj : T w o D O b j e c t ):
s e l f . c o n t e n t s . r e m o v e ( obj )
...
21.12.2021 P. Thiemann – Info I 5 / 41
Aggregie- rung
Properties Operator- Überladung Zusammen- fassung
Die Klasse Composite (2)
Die Methodensize_change,moveundpositionwerden überschrieben.
Wir wälzen das Ändern und Verschieben des zusammengesetzten Objektes auf die Einzelobjekte ab:Delegieren.
newgeoclasses.py (2)
d e f s i z e _ c h a n g e ( self , p e r c e n t : f l o a t):
f o r obj in s e l f . c o n t e n t s : obj . s i z e _ c h a n g e ( p e r c e n t )
d e f m o v e ( self , x c h a n g e : float, y c h a n g e : f l o a t):
f o r obj in s e l f . c o n t e n t s : obj . m o v e ( xc h an ge , y c h a n g e ) d e f p o s i t i o n ( s e l f ):
if s e l f . c o n t e n t s :
r e t u r n s e l f . c o n t e n t s [ 0 ] . p o s i t i o n () r e t u r n s u p e r(). p o s i t i o n ()
21.12.2021 P. Thiemann – Info I 6 / 41
Aggregie- rung
Properties Operator- Überladung Zusammen- fassung
Die Klasse Composite (3)
Python-Interpreter
>>> c = Circle(x=1,y=2); r = Rectangle(height=10,width=10)
>>> a = Composite()
>>> a.add([r, c])
>>> a.size_change(200)
>>> r.area() 400.0
>>> a.move(40,40)
>>> a.position() (40, 40)
>>> c.position() (41, 42)
>>> b = Composite()
>>> a.add([b])
>>> a.move(-10, -10)
>>> b.position()
21.12.2021 P. Thiemann – Info I 7 / 41
Aggregie- rung
Properties Operator- Überladung Zusammen- fassung
Vererbung und Komposition
Aggregie- rung Properties Operator- Überladung Zusammen- fassung
2 Properties
21.12.2021 P. Thiemann – Info I 10 / 41
Aggregie- rung Properties Operator- Überladung Zusammen- fassung
Zugriff auf Attribute kontrollieren:
Getter und Setter
Ziel ist dieKontrolleüber das Abfragen und Setzen von Attributwerten.
Invarianten zwischen Attributwerten sollen respektiert werden.
Es soll nicht möglich sein, unsinnige Attributwerte zu setzen.
Der Zustand eines Objekts soll gekapselt werden.
In anderen Sprachen können Attribute alsprivatdeklariert werden.
Nur Methoden des zugehörigen Objekts können sie lesen bzw. ändern.
Sie sind unsichtbar für Objekte anderer Klassen.
⇒ Datenkapselung;Invariantenkönnen garantiert werden.
Für den Zugriff durch andere Objekte werden (häufig)Getter- und (seltener) Setter-Methoden bereitgestellt.
Eine Getter-Methode liest ein privates Attribut.
Eine Setter-Methode schreibt ein privates Attribut.
In Python sind Attribute im wesentlichenöffentlich, aber sie können durch Getter und Setter alsPropertiesgeschützt werden.
Aggregie- rung Properties Operator- Überladung Zusammen- fassung
Datenkapselung und Invarianten
Definition: Dateninvariante
Eine Dateninvariante ist eine logische Aussage über die Attribute eines Objekts, die während der gesamten Lebensdauer des Objekts erfüllt sein muss.
Der Konstruktor muss die Dateninvariante sicherstellen.
Die Methoden müssen die Dateninvariante erhalten.
Unbewachtes Ändern eines Attributs kann die Dateninvariante zerstören.
Definition: Datenkapselung
Attribute (Objektzustand) können nicht direkt gelesen oder geändert werden.
Die Interaktion mit einem Objekt geschieht nur durch Methoden.
Die Implementierung (Struktur des Objektzustands) kann verändert werden, ohne dass andere Teile des Programms geändert werden müssen.
21.12.2021 P. Thiemann – Info I 12 / 41
Aggregie- rung Properties Operator- Überladung Zusammen- fassung
Beispiel Invariante: Radius eines Kreises
Invariante
Das Attributradiusder KlasseCirclesoll immer größer als Null sein.
Regel 1:Jede Invariantemussim docstring der Klasse dokumentiert sein!
@ d a t a c l a s s
c l a s s C i r c l e ( T w o D O b j e c t ):
' ' ' R e p r e s e n t s a c i r c l e in t h e p l a n e . A t t r i b u t e s :
r a d i u s : a n u m b e r i n d i c a t i n g t h e r a d i u s of t h e c i r c l e x , y : i n h e r i t e d f r o m T w o D O b j e c t
In var i a n t s : r a d i u s > 0 ' ' '
r a d i u s : f l o a t
Aggregie- rung Properties Operator- Überladung Zusammen- fassung
Beispiel Invariante: Radius eines Kreises
Der docstring kann Verletzungen der Invariante nicht verhindern. . . Regel 2:Der Konstruktor muss die Einhaltung der Invariante prüfen!
Die Prüfung geschieht durch eine Assertion in einer spezielle Methode __post_init__. Verletzung führt zu einerException(Ausnahme).
__post_init__wird automatisch bei Konstruktion einer Instanz einer Datenklasse aufgerufen.
@ d a t a c l a s s
c l a s s C i r c l e ( T w o D O b j e c t ):
...
r a d i u s : f l o a t
d e f _ _ p o s t _ i n i t _ _ ( s e l f ):
a s s e r t s e l f . r a d i u s > 0 , " r a d i u s ␣ s h o u l d ␣ be ␣ g r e a t e r ␣ t h a n ␣ 0 "
21.12.2021 P. Thiemann – Info I 14 / 41
Aggregie- rung Properties Operator- Überladung Zusammen- fassung
Was passiert?
Bei falschem Aufruf des Konstruktors wird eine Exception ausgelöst.
Python-Interpreter
>>> c = Circle (x=10,y=20, radius=-3) Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File ".../properties.py", line 46, in __init__
assert radius > 0, "radius should be greater than 0"
AssertionError: radius should be greater than 0
Aggregie- rung Properties Operator- Überladung Zusammen- fassung
Beispiel: Radius eines Kreises
Ein böswilliger Mensch kann immer noch folgenden Code schreiben:
c = C i r c l e ( x =20 , y =20 , r a d i u s =5)
c . r a d i u s = -3 # # o b j e c t in var i a n t b r o k e n
Regel 3:Das Attributradiusmuss als Property ohne Setter definiert werden!
@ d a t a c l a s s
c l a s s C i r c l e ( T w o D O b j e c t ):
...
_ _ r a d i u s : f l o a t # # N a m e n v o n P r o p e r t i e s b e g i n n e n m i t __
d e f _ _ p o s t _ i n i t _ _ ( s e l f ):
a s s e r t s e l f . _ _ r a d i u s > 0 , " r a d i u s ␣ s h o u l d ␣ be ␣ g r e a t e r ␣ t h a n ␣ 0 "
@ p r o p e r t y
d e f r a d i u s ( s e l f ) - > f l o a t: r e t u r n s e l f . _ _ r a d i u s
21.12.2021 P. Thiemann – Info I 16 / 41
Aggregie- rung Properties Operator- Überladung Zusammen- fassung
Was passiert?
Der Attributwert für den Radius wird im Feld__radiusdes Objekts gespeichert.
Felder, deren Name mit__beginnt, sind von außen nicht ohne weiteres zugreifbar!
radiusist eine normaleMethode, derGetterfürradius.
Die Dekoration mit@propertybewirkt, dassradiuswie ein Attribut verwendet werden kann.
Ein Attributzugriffc.radiuswird als Methodenaufrufc.radius()interpretiert.
Python-Interpreter
>>> c = Circle (x=10,y=20, radius=3)
>>> c.radius 3
>>> c.x = -3
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: can’t set attribute
Aggregie- rung Properties Operator- Überladung Zusammen- fassung
Zusammenfassung Invariante
Eine (Daten-) Invariante ist eine logische Aussage über die Attribute eines Objekts, die während der gesamten Lebensdauer des Objekts erfüllt sein muss.
Regeln zu Dateninvarianten
1 Jede Invariante muss im docstring der Klasse dokumentiert sein!
2 Der Konstruktor muss die Einhaltung der Invariante prüfen!
3 Die Attribute, die in der Invariante erwähnt werden, müssen als Properties ohne Setter definiert werden!
21.12.2021 P. Thiemann – Info I 18 / 41
Aggregie- rung Properties Operator- Überladung Zusammen- fassung
Beispiel: Datenkapselung
Aufgabe
Ein Zeichenprogramm verwendet Punkte in der Ebene. Eine wichtige Operation auf Punkten ist die Drehung (um den Ursprung) um einen bestimmten Winkel.
Erster Versuch
@ d a t a c l a s s c l a s s P o i n t 2 D :
x : f l o a t y : f l o a t
d e f t u r n ( self , phi : f l o a t):
s e l f . x , s e l f . y = ( s e l f . x * cos ( phi ) - s e l f . y * sin ( phi ) , s e l f . x * sin ( phi ) + s e l f . y * cos ( phi ))
Aggregie- rung Properties Operator- Überladung Zusammen- fassung
Was passiert?
Python-Interpreter
>>> pp = Point2D(1,0)
>>> pp.x, pp.y (1, 0)
>>> pp.turn(pi/2)
>>> pp.x, pp.y
(6.123233995736766e-17, 1.0)
>>> pp.y = -1
>>> pp.turn (pi/2)
>>> pp.x, pp.y (1.0, 0.0)
21.12.2021 P. Thiemann – Info I 20 / 41
Aggregie- rung Properties Operator- Überladung Zusammen- fassung
Beobachtungen
Das Interface vonPoint2DObjekten besteht aus den Attributenx,yund der Methodeturn().
Jeder Aufruf vonturn()erfordert vier trigonometrische Operationen (naja, mindestens zwei), die aufwändig zu berechnen sind.
Möglichkeit zur Vermeidung der trigonometrischen Operationen:
Ändere die Datenrepräsentationvon rechtwinkligen Koordinaten (x, y) in Polarkoordinaten(r,ϑ). In Polarkoordinaten entspricht eine Drehung umϕ der Addition der Winkelϑ+ϕ.
Aber: das Interface soll erhalten bleiben!
Ein Fall für Datenkapselung mit GetternundSettern!
(keine Invariante:xundysind beliebigefloatZahlen!)
Aggregie- rung Properties Operator- Überladung Zusammen- fassung
Datenkapselung: Änderung der Repräsentation ohne Änderung des Interface
@ d a t a c l a s s
c l a s s P o i n t P o l a r : x : I n i t V a r [f l o a t] y : I n i t V a r [f l o a t]
d e f _ _ p o s t _ i n i t _ _ ( self , x :float, y :f l o a t):
s e l f . __r = s q r t ( x * x + y * y ) s e l f . _ _ t h e t a = a t a n 2 ( y , x ) d e f t u r n ( self , phi :f l o a t):
s e l f . _ _ t h e t a += phi
...
xundydefinieren nur die Parameter für den Konstruktor (Effekt vonInitVar) Interne Repräsentation durch Polarkoordinaten
Interne Attribute__rund__thetavon außen nicht zugreifbar
21.12.2021 P. Thiemann – Info I 22 / 41
Aggregie- rung Properties Operator- Überladung Zusammen- fassung
Datenkapselung: Interface rechtwinklige Koordinaten
@ p r o p e r t y
d e f x ( s e l f ) - > f l o a t:
r e t u r n s e l f . __r * cos ( s e l f . _ _ t h e t a )
@ p r o p e r t y
d e f y ( s e l f ) - > f l o a t:
r e t u r n s e l f . __r * sin ( s e l f . _ _ t h e t a )
@x . s e t t e r
d e f x ( self , x : f l o a t):
s e l f . _ _ p o s t _ i n i t _ _ ( x , s e l f . y )
@y . s e t t e r
d e f y ( self , y : f l o a t):
s e l f . _ _ p o s t _ i n i t _ _ ( s e l f . x , y )
Definition der Getter wie gehabt.
Definition der Setter dekoriert mit@x.setter, wobeixder Propertyname ist.
Methodendefinition für den Propertynamen mit einem Parameter (+ self).
Eine Zuweisungp.x = vwird interpretiert als Methodenaufrufp.x(v).
Aggregie- rung Properties Operator- Überladung Zusammen- fassung
Was passiert? Exakt das Gleiche wie mit
Point2D!
Python-Interpreter
>>> pp = PointPolar(1,0)
>>> pp.x, pp.y (1, 0)
>>> pp.turn(pi/2)
>>> pp.x, pp.y
(6.123233995736766e-17, 1.0)
>>> pp.y = -1
>>> pp.turn (pi/2)
>>> pp.x, pp.y (1.0, 0.0)
21.12.2021 P. Thiemann – Info I 24 / 41
Aggregie- rung Properties Operator- Überladung Zusammen- fassung
Weitere Möglichkeiten
Intern könnte der PunktbeideRepräsentationen unterstützen.
Nur die jeweils benötigte Repräsentation wird berechnet.
Transformationen werden immer in der günstigsten Repräsentation ausgeführt:
Rotation in Polarkoordinaten, Translation in rechtwinkligen Koordinaten, usw.
Aggregie- rung Properties Operator- Überladung
Arithmetische Operatoren Vergleichsoperato- ren
Zusammen- fassung
3 Operator-Überladung
Arithmetische Operatoren Vergleichsoperatoren
21.12.2021 P. Thiemann – Info I 27 / 41
Aggregie- rung Properties Operator- Überladung
Arithmetische Operatoren Vergleichsoperato- ren
Zusammen- fassung
Überladung von Operatoren
EinOperatoristüberladen(operator overloading), wenn dieser Operator je nach Typ der Argumente (und ggf. dem Kontext) unterschiedlich definiert ist.
Traditionell sind die arithmetischen Operatoren in vielen Programmiersprachen für alle numerischen Typen überladen.
In Python sind außerdem die Operatoren „+“ und „*“ für Strings überladen.
Für gewisse Operatoren können wir Überladung selbst definieren!
Überladung ist immer mit Vorsicht zu geniessen:
Im Programmtext ist es nicht mehr offensichtlich, welcher Code ausgeführt wird, wenn überladene Operatoren vorkommen.
Eine Überladung darf nicht “die Intuition” eines Operators verletzen.
Beispiel: „+“ (auf Zahlen) hat Eigenschaften wie Kommutativität, Assoziativität, 0 als neutrales Element, etc, die durch Überladung nicht gestört werden sollten.
Aggregie- rung Properties Operator- Überladung
Arithmetische Operatoren Vergleichsoperato- ren
Zusammen- fassung
Beispiel: Addition für 2D-Punkte
point2d.py (1)
c l a s s P o i n t 2 D : ...
d e f _ _ a d d _ _ ( self , o t h e r ):
r e t u r n P o i n t 2 D ( s e l f . x + o t h e r . x , s e l f . y + o t h e r . y )
Die dunder1Methode__add__definiert die Überladung des „+“-Operators.
Wennpp = Point2D (...), dann wird eine “Addition”pp + vals Methodenaufrufpp.__add__(v)interpretiert.
Was fehlt hier?
Was passiert, wennotherkeine Instanz vonPoint2Dist?
1dunder = double underline
21.12.2021 P. Thiemann – Info I 29 / 41
Aggregie- rung Properties Operator- Überladung
Arithmetische Operatoren Vergleichsoperato- ren
Zusammen- fassung
Beispiel: Addition für 2D-Punkte
point2d.py
c l a s s P o i n t 2 D : ...
d e f _ _ a d d _ _ ( self , o t h e r : P o i n t 2 D ):
if i s i n s t a n c e ( other , P o i n t 2 D ):
r e t u r n P o i n t 2 D ( s e l f . x + o t h e r . x , s e l f . y + o t h e r . y ) e l s e:
r a i s e T y p e E r r o r ( " C a n n o t ␣ add ␣ P o i n t 2 D ␣ and ␣ " + s t r (t y p e ( o t h e r )))
Der Funktionsaufrufisinstance (other, Point2D)testet, obothereine Instanz vonPoint2Dist.
Falls nicht, wird hier eine Exception erzeugt.
Aggregie- rung Properties Operator- Überladung
Arithmetische Operatoren Vergleichsoperato- ren
Zusammen- fassung
Beispiel: Multiplikation für 2D-Punkte
mit den dunder Methoden__mul__und__rmul__
point2d.py
c l a s s P o i n t 2 D : ...
d e f _ _ m u l _ _ ( self , o t h e r : U n i o n [ Po i nt 2D , n u m b e r s . N u m b e r ]):
if i s i n s t a n c e ( other , P o i n t 2 D ): # s c a l a r p r o d u c t r e t u r n s e l f . x * o t h e r . x + s e l f . y * o t h e r . y
e l i f i s i n s t a n c e ( other , n u m b e r s . N u m b e r ): # s c a l a r m u l t i p l i c a t i o n r e t u r n P o i n t 2 D ( o t h e r * s e l f . x , o t h e r * s e l f . y )
e l s e:
r a i s e T y p e E r r o r ( " C a n n o t ␣ m u l t i p l y ␣ P o i n t 2 D ␣ and ␣ " + s t r (t y p e ( o t h e r ))) d e f _ _ r m u l _ _ ( self , o t h e r : n u m b e r s . N u m b e r ):
if i s i n s t a n c e ( other , n u m b e r s . N u m b e r ):
r e t u r n P o i n t 2 D ( o t h e r * s e l f . x , o t h e r * s e l f . y ) e l s e:
r a i s e T y p e E r r o r ( " C a n n o t ␣ m u l t i p l y ␣ " + s t r (t y p e ( o t h e r )) + " ␣ and ␣ P o i n t 2 D " )
21.12.2021 P. Thiemann – Info I 31 / 41
Aggregie- rung Properties Operator- Überladung
Arithmetische Operatoren Vergleichsoperato- ren
Zusammen- fassung
Was passiert?
Python-Interpreter
>>> p1 = Point2D (1,0)
>>> p1.x, p1.y (1, 0)
>>> p2 = p1 * 42 # multiply p1 with a number
>>> p2.x, p2.y # yields a point (42, 0)
p1 * 42entsprichtp1.__mul__(42);otherist eine Zahl
Aggregie- rung Properties Operator- Überladung
Arithmetische Operatoren Vergleichsoperato- ren
Zusammen- fassung
Multiplikation (2)
Python-Interpreter
>>> w = p1 * p2 # multiply two points
>>> w # yields a number 42
p1 * p2entsprichtp1.__mul__(p2);otherist eine Instanz vonPoint2D
21.12.2021 P. Thiemann – Info I 33 / 41
Aggregie- rung Properties Operator- Überladung
Arithmetische Operatoren Vergleichsoperato- ren
Zusammen- fassung
Multiplikation (3)
Python-Interpreter
>>> p3 = 3 * p1 # multiply a number with a point
>>> p3.x, p3.y # yields a point (3, 0)
3 * p1 entspricht . . .
3.__mul__(p1). . . —im Prinzip; kann so nicht eingegeben werden aber der Typintkann nicht mit einemPoint2Dmultiplizieren. Daher liefert dieser Versuchden WertNotImplemented.
Daraufhin versucht es Python mit vertauschten Operanden . . . p1.__rmul__(3). . . was ein Ergebnis liefert.
Die arithmetischen Operatoren+,*,-,/und%können nach dem gleichen Muster überladen werden.
Aggregie- rung Properties Operator- Überladung
Arithmetische Operatoren Vergleichsoperato- ren
Zusammen- fassung
Vergleichsoperatoren
Die Vergleichsoperatoren==und!=können mit den dunder Methoden __eq__und__ne__definiert werden.
Sinnvolle Anwendung von Überladung, da für jeden Typ eine andere Implementierung der Gleichheit erforderlich ist!
21.12.2021 P. Thiemann – Info I 35 / 41
Aggregie- rung Properties Operator- Überladung
Arithmetische Operatoren Vergleichsoperato- ren
Zusammen- fassung
Vergleich: __eq__, __ne__
obj.__eq__(other)
Auswertung vonobj == other.
Auswertung vonother == obj, fallsotherkeine__eq__Methode besitzt.
obj.__ne__(other)
Auswertung vonobj != other(oderother != obj).
Der Aufruf von!=gibt automatisch das Gegenteil vom Aufruf von==zurück, außer wenn==das ErgebnisNotImplementedliefert. Es reicht also,
obj.__eq__(other)zu implementieren.
Ohne diese Methoden werden Objekte nur auf Identität verglichen, d.h.x ==
ygdw.x is y.
Aggregie- rung Properties Operator- Überladung
Arithmetische Operatoren Vergleichsoperato- ren
Zusammen- fassung
Gleichheit für 2D Punkte
Equality
@ d a t a c l a s s c l a s s P o i n t 2 D :
...
d e f _ _ e q _ _ ( self , o t h e r ):
r e t u r n (i s i n s t a n c e( other , P o i n t 2 D ) a n d
s e l f . x == o t h e r . x a n d s e l f . y == o t h e r . y )
Datenklassen haben automatisch eine Methode__eq__, falls nicht explizit eine definiert wird.
Das Beispiel zeigt die Methode__eq__, wie sie für die DatenklassePoint2D automatisch erzeugt wird.
21.12.2021 P. Thiemann – Info I 37 / 41
Aggregie- rung Properties Operator- Überladung
Arithmetische Operatoren Vergleichsoperato- ren
Zusammen- fassung
Vergleich: __ge__, __gt__, __le__,__lt__
obj.__ge__(other)
Zur Auswertung vonobj >= other.
Zur Auswertung vonother <= obj, fallsotherüber keine__le__-Methode verfügt.
obj.__gt__(other),obj.__le__(other),obj.__lt__(other): Analog fürobj > otherbzw.obj <= otherbzw.obj < other.
Auch die Vergleichsmethoden können automatisch durch die Datenklasse erzeugt werden, wennorder=Trueangegeben wird:
@ d a t a c l a s s ( o r d e r = T r u e ) c l a s s P o i n t 2 D :
x : f l o a t y : f l o a t
Aggregie- rung Properties Operator- Überladung Zusammen- fassung
4 Zusammenfassung
21.12.2021 P. Thiemann – Info I 40 / 41
Aggregie- rung Properties Operator- Überladung Zusammen- fassung
Zusammenfassung
Aggregierungliegt vor, falls Attribute von Objekten selbst wieder Objekte sind.
Propertieserlauben die Realisierung vonInvariantenundDatenkapselung.
Attributzugriffe werden über Getter und Setter (Methoden) abgewickelt.
Überladungliegt vor, wenn ein Operator die anzuwendende Operation anhand des Typs der Operanden bestimmt.
Python verwendetdunder Methodenzur Implementierung der Überladung von Operatoren.