Was ist der Zweck von __slots__
in Python - vor allem im Hinblick darauf, wann ich es verwenden möchte und wann nicht?
Antworten
Zu viele Anzeigen?Was ist in Python der Zweck von
__slots__
und in welchen Fällen sollte man dies vermeiden?
TLDR:
Das besondere Attribut __slots__
ermöglicht es Ihnen, explizit anzugeben, welche Instanzattribute Sie von Ihren Objektinstanzen erwarten und welche Ergebnisse Sie erwarten:
- schneller Zugriff auf das Attribut.
- Platzersparnis in Erinnerung.
Die Platzersparnis ergibt sich aus
- Speichern von Wertreferenzen in Slots anstelle von
__dict__
. - Verweigerung
__dict__
y__weakref__
Erstellung, wenn übergeordnete Klassen sie verweigern und Sie erklären__slots__
.
Kurze Warnungen
Kleiner Vorbehalt: Sie sollten einen bestimmten Slot nur einmal in einem Vererbungsbaum deklarieren. Zum Beispiel:
class Base:
__slots__ = 'foo', 'bar'
class Right(Base):
__slots__ = 'baz',
class Wrong(Base):
__slots__ = 'foo', 'bar', 'baz' # redundant foo and bar
Python erhebt keine Einwände, wenn Sie dies falsch machen (das sollte es wahrscheinlich auch), Probleme treten sonst vielleicht nicht auf, aber Ihre Objekte nehmen mehr Platz ein, als sie eigentlich sollten. Python 3.8:
>>> from sys import getsizeof
>>> getsizeof(Right()), getsizeof(Wrong())
(56, 72)
Das liegt daran, dass der Slot-Deskriptor der Basis einen eigenen Slot hat, der von dem des Wrongs getrennt ist. Normalerweise sollte das nicht vorkommen, aber es könnte:
>>> w = Wrong()
>>> w.foo = 'foo'
>>> Base.foo.__get__(w)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: foo
>>> Wrong.foo.__get__(w)
'foo'
Die größte Einschränkung gilt für die Mehrfachvererbung - mehrere "Elternklassen mit nicht leeren Slots" können nicht kombiniert werden.
Um dieser Einschränkung Rechnung zu tragen, sollten Sie bewährte Verfahren anwenden: Entfernen Sie alle Abstraktionen bis auf eine oder alle der Eltern, von denen ihre konkrete Klasse bzw. Ihre neue konkrete Klasse gemeinsam erben wird, und geben Sie der/den Abstraktion(en) leere Slots (genau wie abstrakte Basisklassen in der Standardbibliothek).
Siehe Abschnitt über Mehrfachvererbung unten für ein Beispiel.
Anforderungen:
-
Um Attribute zu haben, die in
__slots__
tatsächlich in Slots gespeichert werden, anstatt in einem__dict__
muss eine Klasse erben vonobject
(automatisch in Python 3, muss aber in Python 2 explizit angegeben werden). -
Um die Schaffung eines
__dict__
müssen Sie erben vonobject
und alle Klassen in der Vererbung müssen deklarieren__slots__
und keiner von ihnen kann eine'__dict__'
Eintrag.
Es gibt eine Menge Details, wenn Sie weiter lesen möchten.
Warum verwenden __slots__
: Schnellerer Zugriff auf Attribute.
Der Erfinder von Python, Guido van Rossum, Staaten die er tatsächlich geschaffen hat __slots__
für einen schnelleren Zugriff auf Attribute.
Es ist trivial, einen messbar deutlich schnelleren Zugriff nachzuweisen:
import timeit
class Foo(object): __slots__ = 'foo',
class Bar(object): pass
slotted = Foo()
not_slotted = Bar()
def get_set_delete_fn(obj):
def get_set_delete():
obj.foo = 'foo'
obj.foo
del obj.foo
return get_set_delete
et
>>> min(timeit.repeat(get_set_delete_fn(slotted)))
0.2846834529991611
>>> min(timeit.repeat(get_set_delete_fn(not_slotted)))
0.3664822799983085
Der Slot-Zugriff ist in Python 3.5 unter Ubuntu fast 30% schneller.
>>> 0.3664822799983085 / 0.2846834529991611
1.2873325658284342
In Python 2 auf Windows habe ich gemessen, dass es etwa 15% schneller ist.
Warum verwenden __slots__
: Speicherplatzeinsparung
Ein weiterer Zweck der __slots__
ist es, den Speicherplatz, den jede Objektinstanz beansprucht, zu verringern.
In meinem eigenen Beitrag zur Dokumentation werden die Gründe dafür klar dargelegt :
Die Platzersparnis gegenüber der Verwendung von
__dict__
können erheblich sein.
SQLAlchemy-Attribute eine Menge Speicherplatz zu sparen __slots__
.
Um dies zu überprüfen, verwenden Sie die Anaconda-Distribution von Python 2.7 auf Ubuntu Linux, mit guppy.hpy
(auch bekannt als heapy) und sys.getsizeof
die Größe einer Klasseninstanz ohne __slots__
deklariert wird, und nichts anderes, beträgt 64 Bytes. Das tut pas umfassen die __dict__
. Vielen Dank Python für die faule Bewertung wieder, die __dict__
wird anscheinend erst dann ins Leben gerufen, wenn sie referenziert wird, aber Klassen ohne Daten sind normalerweise nutzlos. Wenn sie ins Leben gerufen wird, wird die __dict__
Attribut mindestens 280 Byte zusätzlich beträgt.
Im Gegensatz dazu kann eine Klasseninstanz mit __slots__
erklärt zu sein ()
(keine Daten) beträgt nur 16 Byte, und 56 Gesamtbytes mit einem Element in Steckplätzen, 64 mit zwei.
Für 64-Bit-Python zeige ich den Speicherverbrauch in Bytes in Python 2.7 und 3.6, für __slots__
y __dict__
(keine Steckplätze definiert) für jeden Punkt, an dem das Diktat in 3.6 wächst (außer für die Attribute 0, 1 und 2):
Python 2.7 Python 3.6
attrs __slots__ __dict__* __slots__ __dict__* | *(no slots defined)
none 16 56 + 272† 16 56 + 112† | †if __dict__ referenced
one 48 56 + 272 48 56 + 112
two 56 56 + 272 56 56 + 112
six 88 56 + 1040 88 56 + 152
11 128 56 + 1040 128 56 + 240
22 216 56 + 3344 216 56 + 408
43 384 56 + 3344 384 56 + 752
Trotz der kleineren Dicts in Python 3 sehen wir also, wie schön __slots__
für Instanzen skalieren, um Speicherplatz zu sparen, und das ist ein wichtiger Grund, warum Sie die __slots__
.
Nur der Vollständigkeit halber sei darauf hingewiesen, dass pro Slot im Namensraum der Klasse einmalige Kosten von 64 Byte in Python 2 und 72 Byte in Python 3 anfallen, da Slots Datendeskriptoren wie Eigenschaften, sogenannte "Members", verwenden.
>>> Foo.foo
<member 'foo' of 'Foo' objects>
>>> type(Foo.foo)
<class 'member_descriptor'>
>>> getsizeof(Foo.foo)
72
Demonstration von __slots__
:
Die Verweigerung der Schaffung eines __dict__
müssen Sie die Unterklasse object
. Alles Unterklassen object
in Python 3, aber in Python 2 mussten Sie explizit sein:
class Base(object):
__slots__ = ()
jetzt:
>>> b = Base()
>>> b.a = 'a'
Traceback (most recent call last):
File "<pyshell#38>", line 1, in <module>
b.a = 'a'
AttributeError: 'Base' object has no attribute 'a'
oder eine andere Klasse unterordnen, die Folgendes definiert __slots__
class Child(Base):
__slots__ = ('a',)
und jetzt:
c = Child()
c.a = 'a'
sondern:
>>> c.b = 'b'
Traceback (most recent call last):
File "<pyshell#42>", line 1, in <module>
c.b = 'b'
AttributeError: 'Child' object has no attribute 'b'
Zu ermöglichen __dict__
Erstellung beim Unterklassifizieren von geschlitzten Objekten, fügen Sie einfach '__dict__'
zum __slots__
(Beachten Sie, dass die Slots geordnet sind und Sie keine Slots wiederholen sollten, die bereits in übergeordneten Klassen enthalten sind):
class SlottedWithDict(Child):
__slots__ = ('__dict__', 'b')
swd = SlottedWithDict()
swd.a = 'a'
swd.b = 'b'
swd.c = 'c'
et
>>> swd.__dict__
{'c': 'c'}
Oder Sie müssen sich nicht einmal anmelden __slots__
in Ihrer Unterklasse, und Sie werden weiterhin Slots von den Eltern verwenden, aber nicht die Erstellung einer __dict__
:
class NoSlots(Child): pass
ns = NoSlots()
ns.a = 'a'
ns.b = 'b'
Und:
>>> ns.__dict__
{'b': 'b'}
Allerdings, __slots__
kann bei Mehrfachvererbung zu Problemen führen:
class BaseA(object):
__slots__ = ('a',)
class BaseB(object):
__slots__ = ('b',)
Denn das Erstellen einer Kindklasse von Eltern mit beiden nicht leeren Slots schlägt fehl:
>>> class Child(BaseA, BaseB): __slots__ = ()
Traceback (most recent call last):
File "<pyshell#68>", line 1, in <module>
class Child(BaseA, BaseB): __slots__ = ()
TypeError: Error when calling the metaclass bases
multiple bases have instance lay-out conflict
Wenn Sie auf dieses Problem stoßen, können Sie könnte einfach entfernen __slots__
von den Eltern, oder, wenn Sie die Kontrolle über die Eltern haben, geben Sie ihnen leere Slots, oder refactor to Abstraktionen:
from abc import ABC
class AbstractA(ABC):
__slots__ = ()
class BaseA(AbstractA):
__slots__ = ('a',)
class AbstractB(ABC):
__slots__ = ()
class BaseB(AbstractB):
__slots__ = ('b',)
class Child(AbstractA, AbstractB):
__slots__ = ('a', 'b')
c = Child() # no problem!
hinzufügen '__dict__'
a __slots__
um eine dynamische Zuordnung zu erhalten:
class Foo(object):
__slots__ = 'bar', 'baz', '__dict__'
und jetzt:
>>> foo = Foo()
>>> foo.boink = 'boink'
Also mit '__dict__'
in Slots verlieren wir einige der Größenvorteile mit dem Vorteil einer dynamischen Zuweisung und haben immer noch Slots für die Namen, die wir erwarten.
Wenn Sie von einem Objekt erben, das nicht geschlitzt ist, erhalten Sie die gleiche Art von Semantik, wenn Sie __slots__
- Namen, die in __slots__
auf geschlitzte Werte verweisen, während alle anderen Werte in der Instanz __dict__
.
Vermeidung von __slots__
weil Sie in der Lage sein wollen, Attribute spontan hinzuzufügen, ist eigentlich kein guter Grund - fügen Sie einfach "__dict__"
zu Ihrem __slots__
wenn dies erforderlich ist.
Auf ähnliche Weise können Sie auch __weakref__
a __slots__
ausdrücklich, wenn Sie diese Funktion benötigen.
Auf leeres Tupel setzen, wenn ein Namedupel untergeordnet wird:
Die namedtuple builtin machen unveränderliche Instanzen, die sehr leichtgewichtig sind (im Wesentlichen, die Größe der Tupel), aber um die Vorteile zu erhalten, müssen Sie es selbst tun, wenn Sie subclass sie:
from collections import namedtuple
class MyNT(namedtuple('MyNT', 'bar baz')):
"""MyNT is an immutable and lightweight object"""
__slots__ = ()
Verwendung:
>>> nt = MyNT('bar', 'baz')
>>> nt.bar
'bar'
>>> nt.baz
'baz'
Und der Versuch, ein unerwartetes Attribut zuzuweisen, führt zu einer AttributeError
denn wir haben die Schaffung von __dict__
:
>>> nt.quux = 'quux'
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'MyNT' object has no attribute 'quux'
Sie kann ermöglichen __dict__
Erstellung durch Weglassen __slots__ = ()
aber Sie können keine nicht leeren __slots__
mit Subtypen von Tupel.
Größter Vorbehalt: Mehrfachvererbung
Selbst wenn die nicht leeren Slots für mehrere Elternteile gleich sind, können sie nicht zusammen verwendet werden:
class Foo(object):
__slots__ = 'foo', 'bar'
class Bar(object):
__slots__ = 'foo', 'bar' # alas, would work if empty, i.e. ()
>>> class Baz(Foo, Bar): pass
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: Error when calling the metaclass bases
multiple bases have instance lay-out conflict
Die Verwendung eines leeren __slots__
im Elternteil scheint die größte Flexibilität zu bieten, dem Kind die Möglichkeit zu geben, zu verhindern oder zuzulassen, dass (durch Hinzufügen von '__dict__'
um eine dynamische Zuordnung zu erhalten, siehe Abschnitt oben) die Schaffung eines __dict__
:
class Foo(object): __slots__ = ()
class Bar(object): __slots__ = ()
class Baz(Foo, Bar): __slots__ = ('foo', 'bar')
b = Baz()
b.foo, b.bar = 'foo', 'bar'
Sie haben keine haben Slots zu haben - wenn Sie sie also hinzufügen und später wieder entfernen, sollte das keine Probleme verursachen.
Ich lehne mich hier weit aus dem Fenster : Wenn Sie komponieren mixins oder mit abstrakte Basisklassen die nicht instanziiert werden sollen, eine leere __slots__
in diesen Elternteilen scheint im Hinblick auf die Flexibilität für Unterklassler der beste Weg zu sein.
Zur Veranschaulichung erstellen wir zunächst eine Klasse mit Code, den wir im Rahmen der Mehrfachvererbung verwenden möchten
class AbstractBase:
__slots__ = ()
def __init__(self, a, b):
self.a = a
self.b = b
def __repr__(self):
return f'{type(self).__name__}({repr(self.a)}, {repr(self.b)})'
Wir könnten die oben genannten direkt verwenden, indem wir die erwarteten Slots erben und deklarieren:
class Foo(AbstractBase):
__slots__ = 'a', 'b'
Aber das ist uns egal, das ist triviale Einzelvererbung, wir brauchen eine andere Klasse, von der wir auch erben können, vielleicht mit einem lauten Attribut:
class AbstractBaseC:
__slots__ = ()
@property
def c(self):
print('getting c!')
return self._c
@c.setter
def c(self, arg):
print('setting c!')
self._c = arg
Wenn nun beide Basen nicht leere Slots hätten, könnten wir das Folgende nicht tun. (Tatsächlich hätten wir, wenn wir wollten, Folgendes geben können AbstractBase
nicht leere Slots a und b, und ließ sie in der untenstehenden Erklärung weg - sie drin zu lassen wäre falsch):
class Concretion(AbstractBase, AbstractBaseC):
__slots__ = 'a b _c'.split()
Und jetzt haben wir durch Mehrfachvererbung Funktionalität von beiden und können immer noch verweigern __dict__
y __weakref__
Instanziierung:
>>> c = Concretion('a', 'b')
>>> c.c = c
setting c!
>>> c.c
getting c!
Concretion('a', 'b')
>>> c.d = 'd'
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'Concretion' object has no attribute 'd'
Andere Fälle, in denen Steckplätze zu vermeiden sind:
- Vermeiden Sie sie, wenn Sie eine Leistung erbringen wollen
__class__
Zuordnung zu einer anderen Klasse, die sie nicht hat (und man kann sie nicht hinzufügen), es sei denn, die Slot-Layouts sind identisch. (Ich bin sehr daran interessiert, zu erfahren, wer dies tut und warum). - Vermeiden Sie sie, wenn Sie Builtins mit variabler Länge wie long, tuple oder str unterklassifizieren und ihnen Attribute hinzufügen wollen.
- Vermeiden Sie sie, wenn Sie darauf bestehen, Standardwerte über Klassenattribute für Instanzvariablen bereitzustellen.
Möglicherweise können Sie aus dem Rest des Dokuments weitere Vorbehalte herauslesen __slots__
Dokumentation (die 3.7-Entwicklungsdokumente sind die aktuellsten) zu dem ich in letzter Zeit wichtige Beiträge geleistet habe.
Kritiken an anderen Antworten
Die aktuellen Top-Antworten zitieren veraltete Informationen und sind ziemlich hanebüchen und verfehlen in einigen wichtigen Punkten das Ziel.
Verwenden Sie nicht "nur __slots__
bei der Instanziierung vieler Objekte"
Ich zitiere:
"Sie würden gerne die
__slots__
wenn Sie viele (Hunderte, Tausende) Objekte der gleichen Klasse instanziieren werden.
Abstrakte Basisklassen, zum Beispiel aus dem collections
Modul nicht instanziiert werden, aber __slots__
für sie erklärt werden.
Und warum?
Wenn ein Benutzer Folgendes ablehnen möchte __dict__
o __weakref__
Erstellung, dürfen diese Dinge in den übergeordneten Klassen nicht vorhanden sein.
__slots__
trägt zur Wiederverwendbarkeit bei der Erstellung von Schnittstellen oder Mixins bei.
Es ist wahr, dass viele Python-Benutzer nicht für die Wiederverwendbarkeit schreiben, aber wenn Sie es tun, ist die Option, unnötigen Platzverbrauch zu verweigern, wertvoll.
__slots__
macht das Beizen nicht kaputt
Wenn Sie ein geschlitztes Objekt beizen, kann es sein, dass es sich mit einer irreführenden Meldung beschwert TypeError
:
>>> pickle.loads(pickle.dumps(f))
TypeError: a class that defines __slots__ without defining __getstate__ cannot be pickled
Das ist eigentlich falsch. Diese Meldung kommt vom ältesten Protokoll, das die Standardeinstellung ist. Sie können das neueste Protokoll mit der Option -1
Argument. In Python 2.7 wäre dies 2
(das in 2.3 eingeführt wurde), und in 3.6 ist es 4
.
>>> pickle.loads(pickle.dumps(f, -1))
<__main__.Foo object at 0x1129C770>
in Python 2.7:
>>> pickle.loads(pickle.dumps(f, 2))
<__main__.Foo object at 0x1129C770>
in Python 3.6
>>> pickle.loads(pickle.dumps(f, 4))
<__main__.Foo object at 0x1129C770>
Ich würde dies also im Hinterkopf behalten, denn es ist ein gelöstes Problem.
Kritik an der (bis 2. Oktober 2016) akzeptierten Antwort
Der erste Absatz ist halb kurze Erklärung, halb Vorhersage. Hier ist der einzige Teil, der die Frage tatsächlich beantwortet
Die richtige Verwendung von
__slots__
ist es, Platz in Objekten zu sparen. Anstelle eines dynamischen Diktats, das das Hinzufügen von Attributen zu Objekten zu jeder Zeit erlaubt, gibt es eine statische Struktur, die keine Ergänzungen nach der Erstellung zulässt. Dies spart den Overhead eines Diktats für jedes Objekt, das Slots verwendet
Die zweite Hälfte ist eine Wunschvorstellung, die nicht zutrifft:
Dies ist zwar manchmal eine nützliche Optimierung, wäre aber völlig unnötig, wenn der Python-Interpreter dynamisch genug wäre, um das dict nur dann zu benötigen, wenn es tatsächlich Ergänzungen zum Objekt gibt.
Python macht eigentlich etwas Ähnliches, nur dass es die __dict__
wenn auf sie zugegriffen wird, aber die Erstellung vieler Objekte ohne Daten ist ziemlich lächerlich.
Der zweite Absatz vereinfacht zu stark und geht an den tatsächlichen Gründen für die Vermeidung von __slots__
. Im Folgenden wird pas ein echter Grund, Zeitnischen zu vermeiden (für aktuell Gründe, siehe den Rest meiner Antwort oben):
Sie ändern das Verhalten von Objekten, die über Slots verfügen, auf eine Weise, die von Kontrollfreaks und statischen Typisierungswichteln missbraucht werden kann.
Anschließend werden andere Möglichkeiten erörtert, wie dieses perverse Ziel mit Python erreicht werden kann, ohne dass dabei etwas mit __slots__
.
Der dritte Absatz ist eher eine Wunschvorstellung. Insgesamt handelt es sich größtenteils um abwegige Inhalte, die der Antwortende nicht einmal selbst verfasst hat, und trägt dazu bei, Kritikern der Website Munition zu liefern.
Nachweis der Speichernutzung
Erstellen Sie einige normale Objekte und geschlitzte Objekte:
>>> class Foo(object): pass
>>> class Bar(object): __slots__ = ()
Eine Million davon instanziieren:
>>> foos = [Foo() for f in xrange(1000000)]
>>> bars = [Bar() for b in xrange(1000000)]
Inspektion mit guppy.hpy().heap()
:
>>> guppy.hpy().heap()
Partition of a set of 2028259 objects. Total size = 99763360 bytes.
Index Count % Size % Cumulative % Kind (class / dict of class)
0 1000000 49 64000000 64 64000000 64 __main__.Foo
1 169 0 16281480 16 80281480 80 list
2 1000000 49 16000000 16 96281480 97 __main__.Bar
3 12284 1 987472 1 97268952 97 str
...
Zugriff auf die regulären Objekte und ihre __dict__
und kontrollieren Sie erneut:
>>> for f in foos:
... f.__dict__
>>> guppy.hpy().heap()
Partition of a set of 3028258 objects. Total size = 379763480 bytes.
Index Count % Size % Cumulative % Kind (class / dict of class)
0 1000000 33 280000000 74 280000000 74 dict of __main__.Foo
1 1000000 33 64000000 17 344000000 91 __main__.Foo
2 169 0 16281480 4 360281480 95 list
3 1000000 33 16000000 4 376281480 99 __main__.Bar
4 12284 0 987472 0 377268952 99 str
...
Dies steht im Einklang mit der Geschichte von Python, von Vereinheitlichung von Typen und Klassen in Python 2.2
Wenn Sie einen eingebauten Typ unterklassifizieren, wird automatisch zusätzlicher Platz zu den Instanzen hinzugefügt, um die folgenden Elemente unterzubringen
__dict__
y__weakrefs__
. (Die__dict__
wird allerdings erst bei der Verwendung initialisiert, so dass Sie sich keine Gedanken über den Platz machen sollten, der durch ein leeres Wörterbuch für jede von Ihnen erstellte Instanz belegt wird). Wenn Sie diesen zusätzlichen Platz nicht benötigen, können Sie die Phrase "__slots__ = []
" zu Ihrer Klasse.
Zitat Jacob Hallen :
Die richtige Verwendung von
__slots__
ist es, Platz in Objekten zu sparen. Anstatt ein eines dynamischen Diktats, das das Hinzufügen von Attributen zu Objekten jederzeit ermöglicht, gibt es eine statische Struktur, die keine Hinzufügungen nach der Erstellung zulässt. [Diese Verwendung von__slots__
eliminiert den Overhead eines Diktats für jedes Objekt]. Während dies manchmal eine nützliche Optimierung ist, wäre es völlig unnötig, wenn der Python-Interpreter dynamisch genug wäre, so dass er dass er das Diktat nur dann benötigt, wenn es tatsächlich Ergänzungen zum Objekt gibt.Leider haben die Spielautomaten eine Nebenwirkung. Sie verändern das Verhalten von der Objekte, die Slots haben, auf eine Weise, die von Kontrollfreaks missbraucht werden kann und Static Typing Weenies missbraucht werden können. Das ist schlecht, denn die Kontrollfreaks sollten Metaklassen missbrauchen und die statischen Typisierungsfanatiker sollten die Dekoratoren verwenden, da es in Python nur einen offensichtlichen Weg geben sollte, etwas zu tun.
CPython intelligent genug machen, um Platz zu sparen, ohne
__slots__
ist eine wichtige Das ist wahrscheinlich der Grund, warum es (noch) nicht auf der Liste der Änderungen für P3k steht.
Sie würden Folgendes verwenden wollen __slots__
wenn Sie viele (Hunderte, Tausende) Objekte derselben Klasse instanziieren wollen. __slots__
existiert nur als Werkzeug zur Speicheroptimierung.
Es wird dringend davon abgeraten, die __slots__
um die Erstellung von Attributen einzuschränken.
Beizen von Objekten mit __slots__
funktioniert nicht mit dem voreingestellten (ältesten) Pickle-Protokoll; es ist notwendig, eine neuere Version anzugeben.
Einige andere Introspektionsfunktionen von Python können ebenfalls beeinträchtigt werden.
Jedes Python-Objekt hat eine __dict__
atttribute, das ein Wörterbuch ist, das alle anderen Attribute enthält. z.B. wenn Sie eingeben self.attr
python macht eigentlich self.__dict__['attr']
. Wie Sie sich vorstellen können, nimmt die Verwendung eines Wörterbuchs zum Speichern von Attributen zusätzlichen Platz und Zeit für den Zugriff darauf in Anspruch.
Wenn Sie jedoch __slots__
hat jedes Objekt, das für diese Klasse erstellt wird, keine __dict__
zuordnen. Stattdessen erfolgt der gesamte Zugriff auf die Attribute direkt über Zeiger.
Wenn Sie also eher eine Struktur im C-Stil als eine vollwertige Klasse wünschen, können Sie __slots__
zur Verdichtung der Größe der Objekte und zur Verringerung der Zugriffszeit auf die Attribute. Ein gutes Beispiel ist eine Point-Klasse mit den Attributen x und y. Wenn Sie viele Punkte haben werden, können Sie versuchen, mit __slots__
um etwas Speicherplatz zu sparen.
Ergänzend zu den anderen Antworten finden Sie hier ein Beispiel für die Verwendung von __slots__
:
>>> class Test(object): #Must be new-style class!
... __slots__ = ['x', 'y']
...
>>> pt = Test()
>>> dir(pt)
['__class__', '__delattr__', '__doc__', '__getattribute__', '__hash__',
'__init__', '__module__', '__new__', '__reduce__', '__reduce_ex__',
'__repr__', '__setattr__', '__slots__', '__str__', 'x', 'y']
>>> pt.x
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: x
>>> pt.x = 1
>>> pt.x
1
>>> pt.z = 2
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'Test' object has no attribute 'z'
>>> pt.__dict__
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'Test' object has no attribute '__dict__'
>>> pt.__slots__
['x', 'y']
Also, zur Umsetzung __slots__
Es ist nur eine zusätzliche Zeile erforderlich (und Ihre Klasse muss eine New-Style-Klasse sein, falls sie es nicht schon ist). Auf diese Weise können Sie den Speicherbedarf dieser Klassen um das 5-fache reduzieren auf Kosten der Notwendigkeit, eigenen Pickle-Code zu schreiben, falls dies erforderlich sein sollte.
- See previous answers
- Weitere Antworten anzeigen