wake-up-neo.com

Wie bestimme ich die Größe eines Objekts in Python?

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.

560
user46646

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 einen TypeError 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.

553
nosklo

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.

Eine vollständigere Antwort

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 )

Eine vollständigere Funktion

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.

Whitelisted Types, Rekursiver Besucher (alte Implementierung)

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.

279
Aaron Hall

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
77
Mike Dewar

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 ,

Die (Byte-) Codegröße von Objekten wie Klassen, Funktionen, Methoden, Modulen usw. kann durch Setzen der Option _code=True_ angegeben werden.

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 Moduls Class Tracker verwendet und bietet eine Offline-Analyse der Lebensdauer der ausgewählten Anwendung Python Objekte.

70
serv-inc

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.

12
Brian

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
10
wissam

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)) )
8
alexey

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.

7
VonC

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.

1
picmate 涅