Python: Wie funktioniert Vererbung von __slots__ in Subklassen eigentlich?
-
08-07-2019 - |
Frage
In der Python-Datenmodell Referenzabschnitt auf Slots gibt es eine Liste von Hinweise zu __slots__
verwenden. Ich bin gründlich durch die 1. und 6. Artikel verwirrt, weil sie scheinen im Widerspruch zu einander sein.
Erster Eintrag:
- Wenn Sie von einer Klasse erben, ohne
__slots__
, das__dict__
Attribut diese Klasse wird immer zugänglich, so ein__slots__
Definition in der Unterklasse ist bedeutungslos.
Sechster Punkt:
- Die Wirkung eines
__slots__
Erklärung wird auf die Klasse beschränkt wo es definiert ist. Als Ergebnis, Subklassen eine__dict__
haben es sei denn, sie definieren auch__slots__
(Die nur müssen Namen aller enthalten zusätzliche Slots).
Es scheint mir, diese Elemente besser formuliert oder durch Code angezeigt werden können, aber ich habe versucht, meinen Kopf um diese zu wickeln und bin immer noch kommen verwirrt. Ich verstehe, wie __slots__
ist sollte verwendet werden , und ich versuche, einen besseren Griff zu bekommen auf, wie sie funktionieren.
Die Frage:
Kann jemand mir bitte erklären, in einfacher Sprache, was die Bedingungen für die Vererbung von Slots sind, wenn Subklassen?
(Simple Codebeispiele wären hilfreich, aber nicht notwendig.)
Lösung
Wie andere erwähnt haben, der einzige Grund für __slots__
definiert, ist etwas Speicher zu sparen, wenn Sie einfache Objekte mit einem vorgegebenen Satz von Attributen haben und nicht wollen, jeweils um ein Wörterbuch zu tragen. Dies ist nur sinnvoll, für die Klassen, von denen Sie planen, viele Instanzen zu haben, natürlich.
Die Einsparungen können nicht ohne weiteres ersichtlich sein - prüfen ...:
>>> class NoSlots(object): pass
...
>>> n = NoSlots()
>>> class WithSlots(object): __slots__ = 'a', 'b', 'c'
...
>>> w = WithSlots()
>>> n.a = n.b = n.c = 23
>>> w.a = w.b = w.c = 23
>>> sys.getsizeof(n)
32
>>> sys.getsizeof(w)
36
Von diesem würde es scheinen, die mit Schlitzen Größe ist größer als die No-Slots Größe! Aber das ist ein Fehler, denn sys.getsizeof
berücksichtigt nicht „Objektinhalte“ wie das Wörterbuch:
>>> sys.getsizeof(n.__dict__)
140
Da allein die dict 140 Bytes nehmen, eindeutig die „32 Byte“ Objekt n
wird behauptet, ist zu ergreifen, unter Berücksichtigung nicht alles, was in jedem Fall beteiligt ist. Sie können mit Erweiterungen von Drittanbietern einen besseren Job machen wie pympler :
>>> import pympler.asizeof
>>> pympler.asizeof.asizeof(w)
96
>>> pympler.asizeof.asizeof(n)
288
Dies zeigt viel deutlicher den Speicherbedarf, der durch __slots__
gespeichert ist: für ein einfaches Objekt wie dieser Fall ist es ein bisschen weniger als 200 Bytes, fast 2/3 der Gesamtgrundfläche des Objekts. Jetzt, da in diesen Tagen ein Megabyte mehr oder weniger nicht wirklich wichtig, dass alle viel zu den meisten Anwendungen ist dies auch, dass __slots__
erzählt, ist nicht wert, die Mühe, wenn Sie nur ein paar tausend Instanzen um zu einer Zeit haben werden - - jedoch für Millionen von Fällen, tut es sicher einen sehr wichtigen Unterschied machen. Sie können auch eine mikroskopische Speedup (teilweise aufgrund der besseren Cache-Nutzung für kleine Objekte mit __slots__
) erhalten:
$ python -mtimeit -s'class S(object): __slots__="x","y"' -s's=S(); s.x=s.y=23' 's.x'
10000000 loops, best of 3: 0.37 usec per loop
$ python -mtimeit -s'class S(object): pass' -s's=S(); s.x=s.y=23' 's.x'
1000000 loops, best of 3: 0.604 usec per loop
$ python -mtimeit -s'class S(object): __slots__="x","y"' -s's=S(); s.x=s.y=23' 's.x=45'
1000000 loops, best of 3: 0.28 usec per loop
$ python -mtimeit -s'class S(object): pass' -s's=S(); s.x=s.y=23' 's.x=45'
1000000 loops, best of 3: 0.332 usec per loop
, aber das ist etwas abhängig von Python-Version (das sind die Zahlen, die ich mit 2,5 wiederholbar messen, mit 2,6, ich einem größerer relativer Vorteile sehe __slots__
für Einstellung ein Attribut, aber gar keine, in die Tat ein winziger dis Vorteil, für immer it).
Nun, in Bezug auf Vererbung: für eine Instanz dict-weniger, alle Klassen bis seine Vererbungskette muss auch dict-weniger Instanzen hat. Klassen mit dict losen Fällen sind solche, die __slots__
definieren, sowie die meisten eingebauten Typen (eingebauten Typen, deren Instanzen haben dicts sind diejenigen, auf deren Instanzen Sie beliebige Attribute wie Funktionen einstellen können). Überlappungen in Slot-Namen sind nicht verboten, aber sie sind nutzlos und etwas Speicher verschwenden, da Slots vererbt werden:
>>> class A(object): __slots__='a'
...
>>> class AB(A): __slots__='b'
...
>>> ab=AB()
>>> ab.a = ab.b = 23
>>>
Wie Sie sehen, können Sie einstellen a
auf einem AB
Instanz Attribut - AB
selbst definiert nur Slot b
, aber es erbt Schlitz a
von A
. die geerbte Schlitz Repeating ist nicht verboten:
>>> class ABRed(A): __slots__='a','b'
...
>>> abr=ABRed()
>>> abr.a = abr.b = 23
aber verschwendet ein wenig Speicher:
>>> pympler.asizeof.asizeof(ab)
88
>>> pympler.asizeof.asizeof(abr)
96
so gibt es wirklich keinen Grund, es zu tun.
Andere Tipps
class WithSlots(object):
__slots__ = "a_slot"
class NoSlots(object): # This class has __dict__
pass
Erstes Element
class A(NoSlots): # even though A has __slots__, it inherits __dict__
__slots__ = "a_slot" # from NoSlots, therefore __slots__ has no effect
Sechster Punkt
class B(WithSlots): # This class has no __dict__
__slots__ = "some_slot"
class C(WithSlots): # This class has __dict__, because it doesn't
pass # specify __slots__ even though the superclass does.
Sie werden wahrscheinlich nicht __slots__
in naher Zukunft verwenden müssen. Es ist nur auf Kosten einer gewissen Flexibilität speichern Speicher vorgesehen. Es sei denn, Sie Zehntausende von Objekten wird es keine Rolle.
Python: Wie funktioniert Vererbung von
__slots__
in Subklassen tatsächlich funktioniertbin ich gründlich vom 1. und 6. Artikel verwirrt, weil sie sich zu sein scheinen zu widersprechen.
Diese Elemente nicht widersprechen eigentlich einander. Die erste regards Subklassen von Klassen, die __slots__
nicht implementieren, die zweite regards Subklassen von Klassen, die nicht implementieren __slots__
.
Subklassen von Klassen, die __slots__
nicht implementieren
ich zunehmend bewusst bin, dass so groß ist wie die Python-Dokumentation ist (zu Recht) den Ruf, zu sein sie sind nicht perfekt, vor allem in Bezug auf die weniger genutzten Funktionen der Sprache. Ich würde ändern die docs wie folgt:
Wenn Sie von einer Klasse ohne
__slots__
erben, das__dict__
Attribut diese Klasse wird immer zugänglich sein, so eine.__slots__
Definition in die Unterklasse ist sinnlos
__slots__
ist nach wie vor sinnvoll für eine solche Klasse. Es dokumentiert die erwarteten Namen der Attribute der Klasse. Es auch schafft Slots für diese Attribute - sie werden die schnelleren Lookups bekommen und weniger Platz nutzen. Es erlaubt nur für andere Attribute, die die __dict__
zugewiesen werden.
Das Änderung rel="nofollow wurde angenommen und ist nun in der neueste Dokumentation .
Hier ist ein Beispiel:
class Foo:
"""instances have __dict__"""
class Bar(Foo):
__slots__ = 'foo', 'bar'
Bar
hat nicht nur die Schlitze es erklärt, es hat auch Foo Slots - die __dict__
sind:
>>> b = Bar()
>>> b.foo = 'foo'
>>> b.quux = 'quux'
>>> vars(b)
{'quux': 'quux'}
>>> b.foo
'foo'
Subklassen von Klassen, die Sie implementieren __slots__
Die Wirkung einer
__slots__
Erklärung auf die Klasse beschränkt, in denen es ist definiert. Als Ergebnis haben Subklassen eine__dict__
es sei denn, sie auch definieren__slots__
(die nur Namen der zusätzlichen enthalten Slots).
Nun, das ist nicht ganz richtig nicht. Die Wirkung einer __slots__
Erklärung ist nicht ganz beschränkt sich auf die Klasse, in der sie definiert ist. Sie können Auswirkungen auf die Mehrfachvererbung, zum Beispiel.
würde ich das ändern:
Für die Klassen in einer Vererbungsbaum, der
__slots__
definiert, Unterklassen wird eine__dict__
es sei denn, sie haben auch definieren__slots__
(die nur Namen der zusätzlichen enthalten Slots).
Ich habe es tatsächlich aktualisiert zu lesen:
Die Wirkung einer
__slots__
Erklärung ist nicht auf die Klasse beschränkt wo es definiert ist.__slots__
in Eltern erklärt sind in Kind-Klassen. Allerdings wird Kind Subklassen eine__dict__
erhalten und__weakref__
es sei denn, sie auch__slots__
definieren (die nur Namen von zusätzlichen Slots enthalten soll).
Hier ist ein Beispiel:
class Foo:
__slots__ = 'foo'
class Bar(Foo):
"""instances get __dict__ and __weakref__"""
Und wir sehen, dass eine Unterklasse einer geschlitzten Klasse bekommt die Schlitze zu verwenden:
>>> b = Bar()
>>> b.foo = 'foo'
>>> b.bar = 'bar'
>>> vars(b)
{'bar': 'bar'}
>>> b.foo
'foo'
(Weitere Informationen über __slots__
, siehe meine Antwort hier .)
Von der Antwort, die Sie verknüpfen:
Die richtige Verwendung von
__slots__
ist Raum in Objekten zu speichern. Statt ein dynamisches dict mit ...
„Wenn aus einer Klasse ohne __slots__
erben, die __dict__
Attribut dieser Klasse wird immer zugänglich sein“, so Ihre eigene __slots__
Zugabe kann keine Objekte verhindern, dass ein __dict__
mit und kann nicht Platz sparen.
Das Bit über __slots__
nicht vererbt wird, ist ein wenig stumpf. Denken Sie daran, dass es ein magisches Attribut ist und verhält sich nicht wie andere Attribute, dann wieder gelesen, dass dieses magische Schlitze Verhalten mit den Worten, nicht vererbt wird. (Das ist wirklich alles, was es ist.)
Mein Verständnis ist, wie folgt:
-
Klasse
X
hat keine__dict__
<------->
KlasseX
und deren Super alle angegebenen__slots__
-
In diesem Fall werden die tatsächlichen Schlitze der Klasse aus der Vereinigung von
__slots__
Erklärungen besteht fürX
und deren Super; das Verhalten ist nicht definiert (und wird ein Fehler werden), wenn diese Vereinigung nicht disjunkt ist