In C können wir die Größe eines int
, char
usw. finden. Ich möchte wissen, wie die Größe von Objekten wie einem String, einer Ganzzahl usw. in Python ermittelt wird.
Verwandte Frage: Wie viele Bytes pro Element befinden sich in einer Python-Liste (Tupel)?
Ich verwende eine XML-Datei, die Größenfelder enthält, die die Größe des Werts angeben. Ich muss dieses XML analysieren und meine Codierung durchführen. Wenn ich den Wert eines bestimmten Feldes ändern möchte, überprüfe ich das Größenfeld dieses Wertes. Hier möchte ich vergleichen, ob der neue Wert, den ich eingeben möchte, dieselbe Größe wie in XML hat. Ich muss die Größe des neuen Werts überprüfen. Im Falle einer Zeichenfolge kann ich die Länge sagen. Aber im Falle von int, float usw. bin ich verwirrt.
Verwenden Sie einfach die Funktion sys.getsizeof , die im Modul sys
definiert ist.
sys.getsizeof(object[, default])
:Gibt die Größe eines Objekts in Bytes zurück. Das Objekt kann ein beliebiger Objekttyp sein. Alle integrierten Objekte geben korrekte Ergebnisse zurück, dies muss jedoch nicht für Erweiterungen von Drittanbietern gelten, da dies implementierungsspezifisch ist.
Mit dem Argument
default
kann ein Wert definiert werden, der zurückgegeben wird, wenn der Objekttyp keine Möglichkeit zum Abrufen der Größe bietet und einenTypeError
verursachen würde.
getsizeof
ruft die__sizeof__
-Methode des Objekts auf und fügt einen zusätzlichen Garbage Collector-Overhead hinzu, wenn das Objekt vom Garbage Collector verwaltet wird.
Verwendungsbeispiel in python 3.0:
>>> import sys
>>> x = 2
>>> sys.getsizeof(x)
24
>>> sys.getsizeof(sys.getsizeof)
32
>>> sys.getsizeof('this')
38
>>> sys.getsizeof('this also')
48
Wenn Sie sich in python <2.6 befinden und sys.getsizeof
nicht haben, können Sie stattdessen dieses umfangreiche Modul verwenden. Ich habe es aber nie benutzt.
Wie bestimme ich die Größe eines Objekts in Python?
Die Antwort "Verwenden Sie einfach sys.getsizeof" ist keine vollständige Antwort.
Diese Antwort funktioniert direkt für eingebaute Objekte, berücksichtigt jedoch nicht, was diese Objekte enthalten können, insbesondere welche Typen, z. B. benutzerdefinierte Objekte, Tupel , Listen, Dikte und Mengen enthalten. Sie können sowohl Instanzen als auch Zahlen, Zeichenfolgen und andere Objekte enthalten.
Unter Verwendung von 64-Bit-Python 3.6 aus der Anaconda-Distribution habe ich mit sys.getsizeof die Mindestgröße der folgenden Objekte ermittelt und festgestellt, dass Sets und Dikte Speicherplatz vorbelegen, sodass leere erst danach wieder wachsen ein festgelegter Betrag (der je nach Implementierung der Sprache variieren kann):
Python 3:
Empty
Bytes type scaling notes
28 int +4 bytes about every 30 powers of 2
37 bytes +1 byte per additional byte
49 str +1-4 per additional character (depending on max width)
48 Tuple +8 per additional item
64 list +8 for each additional
224 set 5th increases to 736; 21nd, 2272; 85th, 8416; 341, 32992
240 dict 6th increases to 368; 22nd, 1184; 43rd, 2280; 86th, 4704; 171st, 9320
136 func def does not include default args and other attrs
1056 class def no slots
56 class inst has a __dict__ attr, same scaling as dict above
888 class def with slots
16 __slots__ seems to store in mutable Tuple-like structure
first slot grows to 48, and so on.
Wie interpretierst du das? Nehmen wir an, Sie haben ein Set mit 10 Artikeln. Wenn jedes Element jeweils 100 Bytes umfasst, wie groß ist die gesamte Datenstruktur? Die Menge ist 736 selbst, weil sie einmal auf 736 Bytes vergrößert wurde. Dann addieren Sie die Größe der Elemente, sodass insgesamt 1736 Byte vorhanden sind
Einige Einschränkungen für Funktions- und Klassendefinitionen:
Beachten Sie, dass jede Klassendefinition eine Proxy-Struktur __dict__
(48 Byte) für Klassenattrs hat. Jeder Slot hat einen Deskriptor (wie ein property
) in der Klassendefinition.
Geschlitzte Instanzen beginnen mit 48 Bytes in ihrem ersten Element und werden um jeweils 8 Bytes erhöht. Nur leere Objekte mit Slots haben 16 Bytes, und eine Instanz ohne Daten macht nur sehr wenig Sinn.
Außerdem hat jede Funktionsdefinition Codeobjekte, möglicherweise Dokumentzeichenfolgen, und andere mögliche Attribute, sogar einen __dict__
.
Python 2.7-Analyse, bestätigt mit guppy.hpy
und sys.getsizeof
:
Bytes type empty + scaling notes
24 int NA
28 long NA
37 str + 1 byte per additional character
52 unicode + 4 bytes per additional character
56 Tuple + 8 bytes per additional item
72 list + 32 for first, 8 for each additional
232 set sixth item increases to 744; 22nd, 2280; 86th, 8424
280 dict sixth item increases to 1048; 22nd, 3352; 86th, 12568 *
120 func def does not include default args and other attrs
64 class inst has a __dict__ attr, same scaling as dict above
16 __slots__ class with slots has no dict, seems to store in
mutable Tuple-like structure.
904 class def has a proxy __dict__ structure for class attrs
104 old class makes sense, less stuff, has real dict though.
Beachten Sie, dass Wörterbücher ( aber keine Mengen ) eine kompaktere Darstellung in Python 3.6 haben
Ich denke, dass 8 Bytes pro zusätzlichem zu referenzierendem Element auf einer 64-Bit-Maschine sehr viel Sinn machen. Diese 8 Bytes zeigen auf die Stelle im Speicher, an der sich das enthaltene Element befindet. Die 4 Bytes haben eine feste Breite für Unicode in Python 2, wenn ich mich recht entsinne, aber in Python 3 wird str zu einem Unicode mit einer Breite, die der maximalen Breite der Zeichen entspricht.
(Und für mehr über Slots, siehe diese Antwort )
Wir möchten eine Funktion, die die Elemente in Listen, Tupeln, Mengen, Dikten, obj.__dict__
und obj.__slots__
durchsucht, sowie andere Dinge, an die wir vielleicht noch nicht gedacht haben.
Wir möchten uns darauf verlassen, dass gc.get_referents
diese Suche durchführt, da dies auf der C-Ebene funktioniert (was es sehr schnell macht). Der Nachteil ist, dass get_referents redundante Mitglieder zurückgeben kann. Daher müssen wir sicherstellen, dass wir nicht doppelt zählen.
Klassen, Module und Funktionen sind Singletons - sie existieren einmal im Speicher. Wir sind nicht so an ihrer Größe interessiert, da wir nicht viel dagegen tun können - sie sind Teil des Programms. Wir vermeiden es also, sie zu zählen, wenn auf sie verwiesen wird.
Wir werden eine schwarze Liste von Typen verwenden, damit wir nicht das gesamte Programm in unsere Größenzählung einbeziehen.
import sys
from types import ModuleType, FunctionType
from gc import get_referents
# Custom objects know their class.
# Function objects seem to know way too much, including modules.
# Exclude modules as well.
BLACKLIST = type, ModuleType, FunctionType
def getsize(obj):
"""sum size of object & members."""
if isinstance(obj, BLACKLIST):
raise TypeError('getsize() does not take argument of type: '+ str(type(obj)))
seen_ids = set()
size = 0
objects = [obj]
while objects:
need_referents = []
for obj in objects:
if not isinstance(obj, BLACKLIST) and id(obj) not in seen_ids:
seen_ids.add(id(obj))
size += sys.getsizeof(obj)
need_referents.append(obj)
objects = get_referents(*need_referents)
return size
Um dies mit der folgenden Whitelist-Funktion zu vergleichen, wissen die meisten Objekte, wie sie sich zum Zwecke der Garbage Collection selbst durchlaufen (was ungefähr das ist, wonach wir suchen, wenn wir wissen möchten, wie teuer bestimmte Objekte im Speicher sind. Diese Funktionalität wird von verwendet gc.get_referents
.) Diese Maßnahme wird jedoch viel umfangreicher sein, als wir beabsichtigt hatten, wenn wir nicht vorsichtig sind.
Zum Beispiel wissen Funktionen ziemlich viel über die Module, in denen sie erstellt werden.
Ein weiterer Kontrast besteht darin, dass Zeichenfolgen, die Schlüssel in Wörterbüchern sind, normalerweise interniert werden, damit sie nicht dupliziert werden. Wenn Sie nach id(key)
suchen, können Sie auch vermeiden, dass Duplikate gezählt werden, wie im nächsten Abschnitt beschrieben. Die Blacklist-Lösung überspringt das Zählen von Schlüsseln, die Zeichenfolgen sind.
Um die meisten dieser Typen selbst abzudecken, habe ich diese rekursive Funktion geschrieben, um zu versuchen, die Größe der meisten Python -Objekte abzuschätzen, einschließlich der meisten eingebauten Typen im Modul "Collections" und "Custom" Typen (geschlitzt und sonst).
Diese Art von Funktion bietet eine genauere Kontrolle über die Typen, die für die Speichernutzung gezählt werden, birgt jedoch die Gefahr, dass Typen ausgelassen werden:
import sys
from numbers import Number
from collections import Set, Mapping, deque
try: # Python 2
zero_depth_bases = (basestring, Number, xrange, bytearray)
iteritems = 'iteritems'
except NameError: # Python 3
zero_depth_bases = (str, bytes, Number, range, bytearray)
iteritems = 'items'
def getsize(obj_0):
"""Recursively iterate to sum size of object & members."""
_seen_ids = set()
def inner(obj):
obj_id = id(obj)
if obj_id in _seen_ids:
return 0
_seen_ids.add(obj_id)
size = sys.getsizeof(obj)
if isinstance(obj, zero_depth_bases):
pass # bypass remaining control flow and return
Elif isinstance(obj, (Tuple, list, Set, deque)):
size += sum(inner(i) for i in obj)
Elif isinstance(obj, Mapping) or hasattr(obj, iteritems):
size += sum(inner(k) + inner(v) for k, v in getattr(obj, iteritems)())
# Check for custom object instances - may subclass above too
if hasattr(obj, '__dict__'):
size += inner(vars(obj))
if hasattr(obj, '__slots__'): # can have __slots__ with __dict__
size += sum(inner(getattr(obj, s)) for s in obj.__slots__ if hasattr(obj, s))
return size
return inner(obj_0)
Und ich habe es eher beiläufig getestet (ich sollte es nicht testen):
>>> getsize(['a', Tuple('bcd'), Foo()])
344
>>> getsize(Foo())
16
>>> getsize(Tuple('bcd'))
194
>>> getsize(['a', Tuple('bcd'), Foo(), {'foo': 'bar', 'baz': 'bar'}])
752
>>> getsize({'foo': 'bar', 'baz': 'bar'})
400
>>> getsize({})
280
>>> getsize({'foo':'bar'})
360
>>> getsize('foo')
40
>>> class Bar():
... def baz():
... pass
>>> getsize(Bar())
352
>>> getsize(Bar().__dict__)
280
>>> sys.getsizeof(Bar())
72
>>> getsize(Bar.__dict__)
872
>>> sys.getsizeof(Bar.__dict__)
280
Bei dieser Implementierung werden Klassendefinitionen und Funktionsdefinitionen aufgeschlüsselt, da wir nicht alle ihre Attribute überprüfen. Da sie jedoch nur einmal im Speicher für den Prozess vorhanden sein sollten, spielt ihre Größe keine große Rolle.
Für numpy Arrays funktioniert getsizeof
nicht - für mich gibt es aus irgendeinem Grund immer 40 zurück:
from pylab import *
from sys import getsizeof
A = Rand(10)
B = Rand(10000)
Dann (in ipython):
In [64]: getsizeof(A)
Out[64]: 40
In [65]: getsizeof(B)
Out[65]: 40
Zum Glück jedoch:
In [66]: A.nbytes
Out[66]: 80
In [67]: B.nbytes
Out[67]: 80000
Das Modul asizeof
des Pakets Pympler kann dies tun.
Verwenden Sie wie folgt:
_from pympler import asizeof
asizeof.asizeof(my_object)
_
Im Gegensatz zu _sys.getsizeof
_ funktioniert es für Ihre selbst erstellten Objekte . Es funktioniert sogar mit Numpy.
_>>> asizeof.asizeof(Tuple('bcd'))
200
>>> asizeof.asizeof({'foo': 'bar', 'baz': 'bar'})
400
>>> asizeof.asizeof({})
280
>>> asizeof.asizeof({'foo':'bar'})
360
>>> asizeof.asizeof('foo')
40
>>> asizeof.asizeof(Bar())
352
>>> asizeof.asizeof(Bar().__dict__)
280
>>> A = Rand(10)
>>> B = Rand(10000)
>>> asizeof.asizeof(A)
176
>>> asizeof.asizeof(B)
80096
_
Wie erwähnt ,
Und wenn Sie eine andere Ansicht von Live-Daten benötigen, wählen Sie Pympler
das Modul
muppy
wird für die Online-Überwachung einer Python -Anwendung und des ModulsClass Tracker
verwendet und bietet eine Offline-Analyse der Lebensdauer der ausgewählten Anwendung Python Objekte.
Dies kann komplizierter sein, als es aussieht, je nachdem, wie Sie die Dinge zählen möchten. Wenn Sie beispielsweise eine Liste mit Ints haben, möchten Sie die Größe der Liste, die die Verweise auf die Ints enthält? (dh nur auflisten, nicht was darin enthalten ist), oder möchten Sie die tatsächlichen Daten einschließen, auf die verwiesen wird, in welchem Fall Sie doppelte Verweise behandeln müssen, und wie Doppelzählungen verhindert werden können, wenn zwei Objekte Verweise auf enthalten das gleiche Objekt.
Vielleicht möchten Sie sich einen der python Speicherprofiler ansehen, z. B. pysizer , um festzustellen, ob sie Ihren Anforderungen entsprechen.
Nachdem ich dieses Problem viele Male selbst erlebt hatte, schrieb ich eine kleine Funktion (inspiriert von der Antwort von @ aaron-hall) und testete, was ich von sys.getsizeof erwartet hätte:
https://github.com/bosswissam/pysize
Wenn Sie sich für die Hintergrundgeschichte interessieren, hier ist sie
BEARBEITEN: Fügen Sie den folgenden Code als Referenz hinzu. Um den aktuellsten Code zu sehen, überprüfen Sie bitte den Github-Link.
import sys
def get_size(obj, seen=None):
"""Recursively finds size of objects"""
size = sys.getsizeof(obj)
if seen is None:
seen = set()
obj_id = id(obj)
if obj_id in seen:
return 0
# Important mark as seen *before* entering recursion to gracefully handle
# self-referential objects
seen.add(obj_id)
if isinstance(obj, dict):
size += sum([get_size(v, seen) for v in obj.values()])
size += sum([get_size(k, seen) for k in obj.keys()])
Elif hasattr(obj, '__dict__'):
size += get_size(obj.__dict__, seen)
Elif hasattr(obj, '__iter__') and not isinstance(obj, (str, bytes, bytearray)):
size += sum([get_size(i, seen) for i in obj])
return size
Hier ist ein kurzes Skript, das ich basierend auf den vorherigen Antworten geschrieben habe, um die Größen aller Variablen aufzulisten
for i in dir():
print (i, sys.getsizeof(eval(i)) )
Python 3.8 (Q1 2019) wird einige der Ergebnisse von sys.getsizeof
ändern, wie hier angekündigt von Raymond Hettinger:
Python-Container sind bei 64-Bit-Builds 8 Byte kleiner.
_Tuple () 48 -> 40
list [] 64 ->56
set() 224 -> 216
dict {} 240 -> 232
_
Dies kommt nach Ausgabe 33597 und Inada Naoki (methane
) s Arbeit um Compact PyGC_Head und PR 704
Diese Idee reduziert die PyGC_Head-Größe auf zwei Wörter .
Derzeit benötigt PyGC_Head drei Wörter ; _
gc_prev
_, _gc_next
_ und _gc_refcnt
_.
- _
gc_refcnt
_ wird beim Sammeln zum Löschen der Testversion verwendet.- _
gc_prev
_ wird zum Verfolgen und Aufheben der Verfolgung verwendet.Wenn wir also vermeiden können, dass während des Löschens der Testversion ein Tracking/Untracking durchgeführt wird, können sich _
gc_prev
_ und _gc_refcnt
_ denselben Speicherplatz teilen.
Siehe commit d5c875b :
Ein Mitglied _
Py_ssize_t
_ aus _PyGC_Head
_ entfernt.
Alle GC-verfolgten Objekte (z. B. Tupel, Liste, Diktat) werden um 4 oder 8 Byte verkleinert.
Wenn Sie nicht die exakte Größe des Objekts benötigen, aber ungefähr wissen möchten, wie groß es ist, können Sie das Programm schnell (und schmutzig) laufen lassen, längere Zeit in den Energiesparmodus wechseln und die Speichernutzung überprüfen (z. B. : Macs Aktivitätsmonitor) durch diesen speziellen python -Prozess. Dies ist hilfreich, wenn Sie versuchen, die Größe eines einzelnen großen Objekts in einem python -Prozess zu ermitteln. Ich wollte zum Beispiel kürzlich die Speichernutzung einer neuen Datenstruktur überprüfen und mit der von Pythons festgelegter Datenstruktur vergleichen. Zuerst habe ich die Elemente (Wörter aus einem großen gemeinfreien Buch) in eine Gruppe geschrieben, dann die Größe des Prozesses überprüft und dann das Gleiche mit der anderen Datenstruktur gemacht. Ich fand heraus, dass der Python -Prozess mit einer Menge doppelt so viel Speicher beansprucht wie die neue Datenstruktur. Auch hier können Sie nicht genau sagen, dass der vom Prozess verwendete Speicher der Größe des Objekts entspricht. Je größer das Objekt wird, desto geringer wird der vom Rest des Prozesses belegte Speicherplatz im Vergleich zur Größe des Objekts, das Sie überwachen möchten.