Slots

Vermeiden dynamisch erstellter Attribute

Computer Slots

Die Attribute von Objekten werden in einem Dictionary __dict__ gespeichert. Wie jedes andere Dictionary hat auch ein Dictionary, das zum Speichern von Attributen verwendet wird, keine feste Anzahl von Elementen. Mit anderen Worten, man kann diesem Dictionary Elemente hinzufügen, nachdem es definiert worden ist, wie wir in unserem Kapitel über Dictionaries gesehen haben. Aus diesem Grund kann man Instanzen von Klassen, die man bereits erstellt hat, dynamisch Attribute hinzufügen, wie wir im folgenden Beispiel sehen können:

class A(object):
    pass
 
a = A()
a.x = 66
a.y = "dynamisch erstelltes Attribut"

Auf das Dictionary, welches die Attribute von a enthält kann folgendermaßen zugegriffen werden:

a.__dict__
Ausgabe: :

{'x': 66, 'y': 'dynamisch erstelltes Attribut'}

Einige fragen sich nun vielleicht, ob man allen Klassen, oder besser den Instanzen der Klaassen, dynamisch Attribute hinzufügen kann. Dies ist nicht der Fall, wie wir am Beispiel der int- und der list-Klasse demonstrieren:

x = 42
x.a = "nicht möglich, es zu tun"
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-1-fc9c5f48e467> in <module>
      1 x = 42
----> 2 x.a = "nicht möglich, es zu tun"
AttributeError: 'int' object has no attribute 'a'
lst = [34, 999, 1001]
lst.a = "vergiss es"
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-2-0edb5dbb1fba> in <module>
      1 lst = [34, 999, 1001]
----> 2 lst.a = "vergiss es"
AttributeError: 'list' object has no attribute 'a'

Hat man eine Klasse bei der man normalerweise nur wenige Instanzen erzeugt, wie beispielsweise die function-Klasse, überwiegen die Vorteile gegenber den Nachteilen. Der zusätzliche Speicherplatz für das Dictionary bringt uns wesentliche Vorteile für das Design unserer Software. Sobald jedoch üblicherweise in einem Programm eine hohe Zahl von Instanzen einer Klasse erzeugt werden müssen, kann sich das Kosten-Nutzen-Verhältnis schnell umkehren. Der zusätzlich benötigte Speicherplatz kann die Ausführung des Programms negativ beeinflussen oder gar verhindern.

Pythons' Slots sind eine gute Möglichkeit, um dieses Platzverbrauchsproblem zu umgehen. Anstatt ein dynamisches __dict__ dictionary zu haben, das das dynamische Hinzufügen von Attributen zu Objekten ermöglicht, bieten Slots eine statische Struktur, die das Hinzufügen nach dem Erstellen einer Instanz verhindert.

Wenn wir eine Klasse entwerfen, können wir Slots verwenden, um die dynamische Erstellung von Attributen zu verhindern. Um Slots zu definieren, müssen Sie eine Liste mit dem Namen __slots__ definieren. Die Liste muss alle Attribute enthalten, die man verwenden will. Alles, was nicht in dieser Liste ist, kann nicht als Attribut verwendet werden. Wir demonstrieren dies in der folgenden Klasse, in der die __slots__-Liste nur den Namen für ein Attribut val enthält.

class S(object):
    __slots__ = ['val']
    def __init__(self, v):
        self.val = v
x = S(42)
print(x.val)
x.new = "nicht möglich"
42
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-4-0163d3859440> in <module>
     10 print(x.val)
     11 
---> 12 x.new = "nicht möglich"
AttributeError: 'S' object has no attribute 'new'

Wenn wir dieses Programm starten, können wir sehen, dass es nicht möglich ist, dynamisch ein neues Attribut zu erstellen. Wir können kein Attribut new erstellen.

Wir haben am Anfang erwähnt, dass Slots eine Verschwendung von Platz mit Objekten verhindern. Seit Python 3.3 ist dieser Vorteil nicht mehr so beeindruckend. Mit Python 3.3 werden Key-Sharing-Dictionarys zum Speichern von Objekten verwendet. Die Attribute der Instanzen können einen Teil ihres internen Speichers untereinander teilen, d. h. den Teil, in dem die Schlüssel und ihre entsprechenden Hashes gespeichert sind. Dies hilft, den Speicherverbrauch von Programmen zu reduzieren, die viele Instanzen nicht eingebauter (non-builtin) Typen erstellen.