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.

X

Fehler melden

X

Suche