Exkurs - Datenkapselung in Python
Öffentliche / private Attribute
Ein Attribut wird in Python zu einem privaten Attribut, wenn der Attributname mit zwei Unterstrichen beginnt und nicht mit Unterstrichen endet. Beginnt der Attributname nicht mit einem Unterstrich, so ist das Attribut öffentlich. Entsprechendes gilt für Methoden.
Zur Verdeutlichung betrachten wir die folgende Implementierung der Klasse Kartenhaufen
:
class Kartenhaufen(object):
def __init__(self):
self.__kartenListe = []
self.__wert = 0
def __kartenwert(self, karte):
if karte[2] == 'A':
ergebnis = 11
elif karte[2] == 'K':
ergebnis = 4
elif karte[2] == 'D':
ergebnis = 3
elif karte[2] == 'B':
ergebnis = 2
elif karte[2] == '1':
ergebnis = 10
elif karte[2] == '9':
ergebnis = 9
elif karte[2] == '8':
ergebnis = 8
elif karte[2] == '7':
ergebnis = 7
return ergebnis
def hinzufuegen(self, karte):
self.__kartenListe = self.__kartenListe + [karte]
self.__wert = self.__wert + self.__kartenwert(karte)
def getKartenListe(self):
return self.__kartenListe
def getWert(self):
return self.__wert
def setKartenListe(self, vorgegebeneKarten):
self.__kartenListe = vorgegebeneKarten
gesamtwert = 0
for karte in self.__kartenListe:
gesamtwert = gesamtwert + self.__kartenwert(karte)
self.__wert = gesamtwert
Die Attribute __kartenListe
und __wert
beginnen hier mit zwei Unterstrichen,
sind also private Attribute.
Zugriffsmethoden
Zugriffsmethoden benötigt man, um auf private Attribute lesend und schreibend zugreifen zu können. Das Programm oben zeigt, wie man solche Methoden implementiert.
Solche Zugriffsmethoden benutzt man, um z. B. privaten Attribut Werte zuzuweisen und um diese Werte anschließend auszulesen.
>>>
>>> meinKartenhaufen = Kartenhaufen()
>>> meinKartenhaufen.hinzufuegen('K-B')
>>> meinKartenhaufen.hinzufuegen('X-9')
>>> meinKartenhaufen.hinzufuegen('H-K')
>>> meinKartenhaufen.getKartenListe()
['K-B', 'X-9', 'H-K']
>>> meinKartenhaufen.setKartenListe(['K-A', 'H-A'])
>>> meinKartenhaufen.getKartenListe()
['K-A', 'H-A']
>>> meinKartenhaufen.getWert()
22
Achtung: Python-Besonderheiten
Python verhält sich bei privaten Attributen (leider) nicht so restriktiv, wie das eigentlich sein sollte. Der folgende Python-Dialog zeigt einige Besonderheiten von Python.
>>> meinKartenhaufen = Kartenhaufen()
>>> meinKartenhaufen.setKartenListe(['K-A', 'H-A'])
>>> meinKartenhaufen.__wert
Traceback (most recent call last):
File ...
meinKartenhaufen.__wert
AttributeError: 'Kartenhaufen' object has no attribute '__wert'
>>> meinKartenhaufen.__dict__
{'_Kartenhaufen__wert': 22, '_Kartenhaufen__kartenListe': ['K-A', 'H-A']}
>>> meinKartenhaufen._Kartenhaufen__wert
22
Wie erwartet kann man auf das private Attribut __wert
des Objekts meinKartenhaufen
nicht zugreifen. Python meldet als Fehler, dass es kein Attribut __wert
gibt.
Der Aufruf meinKartenhaufen.__dict__
verrät, woran das liegt. Ein Aufruf wie meinKartenhaufen.__dict__
listet sämtliche Attribute mit den zugehörigen Attributwerten des betreffenden Objekts auf.
Interessant ist hier, dass sich das private Attribut __wert
hinter einem anderen Namen versteckt.
Wenn man weiß, wie der neue Name - hier _Kartenhaufen__wert
- gebildet wird, dann kann man
auf das betreffende Attribut zugreifen.
Also: Private Attribute werden in Python mit anderen Namen versehen, so dass kein direkter Zugriff möglich ist.
Kennt man den Namen, hinter dem sich ein privates Attribut verbirgt, so kann man durchaus auf dieses Attribut
zugreifen. Python liefert also keinen echten Zugriffsschutz.
Die völlig unrestiktive Art von Python kann zu unerwartetem Verhalten von Objekten und sehr schwer zu findenden Fehlern führen. Der folgende Dialog zeigt ein solches Beispiel.
>>>
>>> meinKartenhaufen = Kartenhaufen()
>>> meinKartenhaufen.__kartenListe
Traceback (most recent call last):
File ...
meinKartenhaufen.__kartenListe
AttributeError: 'Kartenhaufen' object has no attribute '__kartenListe'
>>> meinKartenhaufen.__dict__
{'_Kartenhaufen__wert': 0, '_Kartenhaufen__kartenListe': []}
>>> meinKartenhaufen.__kartenListe = ['X-B']
>>> meinKartenhaufen.__kartenListe
['X-B']
>>> meinKartenhaufen.__dict__
{'__kartenListe': ['X-B'], '_Kartenhaufen__wert': 0, '_Kartenhaufen__kartenListe': []}
Ein erster Zugriff auf das private Attribut __kartenListe
scheitert. Dann aber ist es entgegen aller
Zugriffslogik scheinbar möglich, dem privaten Attribut __kartenListe
einen Wert zuzuweisen.
Der Aufruf meinKartenhaufen.__dict__
erklärt erst, was hier passiert ist.
Neben dem privaten Attribut __kartenListe
,
das sich hinter dem neuen Namen _Kartenhaufen__kartenListe
versteckt, gibt es noch ein
öffentliches Attribut __kartenListe
, auf das man direkt zugreifen kann.
Dieses unerwartete Verhalten liegt letztlich daran, dass Python es erlaubt, Objekten dynamisch neue Attribute hinzuzufügen. Dieses Verhalten lässt sich wie folgt unterbinden.
class Kartenhaufen(object):
__slots__ = ('__kartenListe', '__wert')
def __init__(self):
self.__kartenListe = []
self.__wert = 0
# ... wie bisher ...
Mit dem Attribut __slots__
wird festgelegt, welche Attribute ein Objekt der
betreffenden Klasse haben darf. Im vorliegenden Fall sind das die privaten Attribute __kartenListe
und __wert
. Python meldet einen Fehler, wenn man versucht, ein weiteres Attribut
zu erzeugen. Mit dieser Festlegung ist jetzt das oben gezeigte Verhalten nicht mehr möglich.
>>>
>>> meinKartenhaufen = Kartenhaufen()
>>> meinKartenhaufen.__kartenListe
Traceback (most recent call last):
File ...
meinKartenhaufen.__kartenListe
AttributeError: 'Kartenhaufen' object has no attribute '__kartenListe'
>>> meinKartenhaufen.__kartenListe = ['X-B']
Traceback (most recent call last):
File ...
meinKartenhaufen.__kartenListe = ['X-B']
AttributeError: 'Kartenhaufen' object has no attribute '__kartenListe'
Vereinbarung - Umgang mit privaten Attributen und Methoden
Wir werden im Folgenden bei der Implementierung von Klassen in Python keine Attribute und Methoden als privat deklarieren. Alle Attribute und Methoden sind daher direkt zugänglich. Allerdings werden wir von dem direkten Zugriff in der Regel keinen Gebrauch machen. Nur in begründeten Sonderfällen (wie z.B. zum schnellen Testen) werden wir von dieser Vereinbarung abweichen.