Warum Tupeln nehmen weniger Platz im Speicher als Listen?
Einen tuple
nimmt weniger Speicherplatz in Python:
>>> a = (1,2,3)
>>> a.__sizeof__()
48
in der Erwägung, dass list
s braucht mehr Speicherplatz:
>>> b = [1,2,3]
>>> b.__sizeof__()
64
Was geschieht intern auf die Python Speicherverwaltung?
- Ich bin mir nicht sicher, wie das intern funktioniert, aber die Liste Objekt hat zumindest mehr Funktionen, wie z.B. Anhängen, die die Tupel nicht haben. Es ist daher sinnvoll, wenn das Tupel als eine einfachere Art des Objekts, die kleiner sein
- Ich denke, es hängt auch von Maschine zu Maschine ....für mich, wenn ich überprüfen a = (1,2,3) dauert 72 und b = [1,2,3] nimmt 88.
- Python Tupel sind unveränderlich. Veränderliche Objekte haben zusätzliche overhead Umgang mit runtime-änderungen.
- die Anzahl der Methoden, die ein Typ hat keine Auswirkungen auf den Speicherplatz der Instanzen nehmen. Die Methode Liste aus, und Ihre Codes werden von den object-Prototyp, aber Instanzen speichern nur die Daten und die internen Variablen.
InformationsquelleAutor JON | 2017-10-10
Schreibe einen Kommentar Antworten abbrechen
Du musst angemeldet sein, um einen Kommentar abzugeben.
Ich nehme an, du bist mit CPython und mit 64 bit (ich habe die gleichen Ergebnisse auf meinem CPython 2.7 64-bit). Es könnte Unterschiede zu anderen Python-Implementierungen, oder wenn Sie eine 32-bit-Python.
Unabhängig von der Implementierung
list
s sind mit variabler Größe, währendtuple
s sind eine Feste Größe hat.So
tuple
s speichern können die Elemente direkt in die Struktur, listet auf der anderen Seite müssen Sie eine Schicht der Dereferenzierung (es speichert einen Zeiger auf die Elemente). Diese Schicht der Dereferenzierung ist ein Zeiger, der auf 64bit-Systemen, das ist 64bit, also 8bytes.Aber es gibt eine Sache, die
list
s tun: Sie über-zuteilen. Ansonstenlist.append
wäre einO(n)
Betrieb immer zu machen, amortisiertO(1)
(viel schneller!!!) es über-weist. Aber jetzt ist es zu verfolgen, die zugeordnet Größe und die gefüllt Größe (tuple
s nur speichern müssen eine Größe, weil reserviert und gefüllt Größe sind immer identisch). Das bedeutet, dass jede Liste zu speichern hat eine andere "Größe", die auf 64-bit-Systeme ist ein 64bit integer, wieder 8 bytes.So
list
s müssen mindestens 16 bytes mehr Speicher alstuple
s. Warum ich sage "mindestens"? Wegen der überallokation. Über-Zuweisung bedeutet, es weist mehr Raum als nötig. Jedoch, der Betrag der über-Zuteilung hängt davon ab, "wie" Sie die Liste erstellen, und fügen Sie/Löschung-Geschichte:Bilder
Ich beschlossen, einige Bilder begleiten die Erklärung oben. Vielleicht sind diese hilfreich
Dies ist, wie es (schematisch) im Arbeitsspeicher gespeichert wird in deinem Beispiel. Ich hob die Unterschiede mit rot (frei hand) Zyklen:
Das ist eigentlich nur eine Annäherung, weil
int
Objekte sind auch Python-Objekte und CPython sogar wiederverwendet kleinen ganzen zahlen, so eine wohl genauere Darstellung (wenn auch nicht so gut lesbar) der Objekte im Speicher wäre:Nützliche links:
tuple
struct in CPython-repository für Python 2.7list
struct in CPython-repository für Python 2.7int
struct in CPython-repository für Python 2.7Beachten Sie, dass
__sizeof__
nicht wirklich zurück die "richtige" Größe! Es gibt nur die Größe der gespeicherten Werte. Allerdings, wenn Sie verwendensys.getsizeof
das Ergebnis ist unterschiedlich:Gibt es 24 "extra" - bytes. Diese sind real, dass der garbage collector Aufwand, der nicht bilanziert, die
__sizeof__
Methode. Das ist, weil Sie sind in der Regel nicht zugelassen, Magie zu verwenden Methoden, die direkt die Funktionen verwenden, die wissen, wie man mit Ihnen umgeht, in diesem Fall:sys.getsizeof
(die eigentlich fügt die GC overhead der zurückgegebene Wert von__sizeof__
).list
memory allocation stackoverflow.com/questions/40018398/...list()
oder eine Liste Verständnis.Nehme ich tiefer in die CPython codebase, damit wir sehen können, wie sich die Größen tatsächlich berechnet. In Ihre spezifischen Beispiel, keine Zuweisungen durchgeführt wurden, also werde ich nicht berühren, dass.
Werde ich für die Verwendung von 64-bit-Werte hier, wie Sie sind.
Größe für
list
s berechnet sich aus der folgenden Funktion,list_sizeof
:Hier
Py_TYPE(self)
ist ein makro, das packt dieob_type
vonself
(RückkehrPyList_Type
), während_PyObject_SIZE
ist ein weiteres makro, das packttp_basicsize
von diesem Typ.tp_basicsize
berechnet alssizeof(PyListObject)
woPyListObject
ist die Instanz-struct.Den
PyListObject
- Struktur hat drei Felder:diese Kommentare haben (die ich gekürzt) erklären, was Sie sind, Folgen Sie den obigen link, um Sie zu Lesen.
PyObject_VAR_HEAD
erweitert, die in drei 8-byte-Felder (ob_refcount
,ob_type
undob_size
) so ein24
byte-Beitrag.So jetzt
res
ist:oder:
Wenn Sie in der Liste Instanz hat Elemente zugeordnet sind. der zweite Teil errechnet sich Ihr Beitrag.
self->allocated
, wie der name schon sagt, hält die Anzahl der zugewiesenen Elemente.Ohne Elemente, die Größe der Listen berechnet werden:
ich.e die Größe der Instanz-struct.
tuple
Objekte definieren nicht eintuple_sizeof
Funktion. Stattdessen nutzen Sieobject_sizeof
zu berechnen, Ihre Größe:Diese, wie für
list
s, schnappt sich dietp_basicsize
und, wenn das Objekt einen nicht-null -tp_itemsize
(D. H. es hat die variable-Länge-Instanzen), es multipliziert die Anzahl der Elemente im Tupel (die er bekommt überPy_SIZE
) mittp_itemsize
.tp_basicsize
wieder verwendetsizeof(PyTupleObject)
wo diePyTupleObject
struct enthält:So, ohne alle Elemente (d.h.
Py_SIZE
zurück0
) die Größe des leeren Tupel ist gleichsizeof(PyTupleObject)
:nicht wahr? Nun, hier ist eine kuriosität, die ich habe nicht gefunden, eine Erklärung für die
tp_basicsize
vontuple
s ist eigentlich wie folgt berechnet:warum eine zusätzliche
8
bytes entfernt isttp_basicsize
ist etwas, was ich noch nicht herausfinden können. (Siehe MSeifert Kommentar für eine mögliche Erklärung)Aber, das ist im Grunde der Unterschied in Ihre spezifischen Beispiel.
list
s auch halten, um eine Anzahl der zugeordneten Elemente, die hilft, festzustellen, wenn über-reservieren Sie wieder.Nun, wenn zusätzliche Elemente Hinzugefügt werden, Listen zu tun, in der Tat diese über-Allokation zu erreichen, um O(1) hängt. Diese Ergebnisse in größeren Größen als MSeifert ist, deckt schön in seiner Antwort.
ob_item[1]
ist meist ein Platzhalter (es macht also Sinn, es ist subtrahiert von der basicsize). Dietuple
zugeordnet wird, die mitPyObject_NewVar
. Ich habe nicht herausgefunden, die details, also das ist nur eine Vermutung...MSeifert Antwort deckt es weitgehend; um es einfach zu halten kann man sich denken:
tuple
unveränderlich ist. Sobald es gesetzt ist, können Sie es nicht ändern. So wissen Sie im Voraus, wie viel Speicher Sie brauchen, um zu reservieren für das Objekt.list
ist veränderlich. Sie können Elemente hinzufügen oder entfernen oder von ihm. Es hat zu wissen, die Größe der es (für interne impl.). Es ändert, wie gebraucht.Es gibt keine kostenlosen Mahlzeiten - diese Fähigkeiten kommt mit einem Preis. Daher der overhead im Speicher für Listen.
Die Größe der Tupel wird ein Präfix vorangestellt, das heißt an Tupel Initialisierung der Dolmetscher genügend Platz für die enthaltenen Daten, und das ist das Ende, was es ist unveränderlich (kann nicht geändert werden), in der Erwägung, dass eine Liste ist ein veränderliches Objekt daher impliziert die dynamische Zuweisung von Speicher, um zu vermeiden, zuweisen von Speicherplatz jedes mal, wenn Sie anfügen oder ändern Sie die Liste ( genügend Raum enthalten, werden die geänderten Daten und kopieren der Daten), weist er den zusätzlichen Raum für Zukunft Anhängen, änderungen, ... , dass ziemlich viel bringt es auf.