Hoe bepaal ik de grootte van een object in Python?

Ik wil weten hoe ik de grootte van objecten zoals een string, integer, etc. in Python kan krijgen.

Verwante vraag: Hoeveel bytes per element zijn er in een Python-lijst (tuple)?

Ik gebruik een XML-bestand dat groottevelden bevat die de grootte van de waarde specificeren. Ik moet deze XML ontleden en mijn codering doen. Als ik de waarde van een bepaald veld wil wijzigen, controleer ik de grootte van het veld van die waarde. Hier wil ik vergelijken of de nieuwe waarde die ik ga invoeren even groot is als in XML. Ik moet de grootte van de nieuwe waarde controleren. In het geval van een string kan ik zeggen dat het de lengte is. Maar in het geval van int, float, etc. ben ik in de war.


Antwoord 1, autoriteit 100%

Gebruik gewoon de sys.getsizeoffunctie gedefinieerd in de sys-module.

sys.getsizeof(object[, default]):

Retourneer de grootte van een object in bytes.
Het object kan elk type object zijn.
Alle ingebouwde objecten keren terug
correcte resultaten, maar dit niet
moeten gelden voor derden
extensies zoals het is implementatie
specifiek.

Alleen het geheugengebruik dat direct aan het object wordt toegeschreven, is
verantwoordelijk, niet het geheugengebruik van objecten waarnaar het verwijst.

Het argument defaultmaakt het mogelijk om te definiëren
een waarde die wordt geretourneerd als de
objecttype biedt geen middelen om
de grootte ophalen en zou leiden tot een
TypeError.

getsizeofroept de object s . aan
__sizeof__methode en voegt een extra overhead voor garbage collector toe
als het object wordt beheerd door de
vuilnisophaler.

Zie recursieve sizeof receptvoor een voorbeeld van het gebruik van getsizeof()recursief om de grootte van containers en al hun inhoud te vinden.

Gebruiksvoorbeeld 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

Als je in python bent < 2.6 en niet over sys.getsizeofbeschikt, kunt u deze uitgebreide modulein plaats daarvan. Heb het echter nooit gebruikt.


Antwoord 2, autoriteit 60%

Hoe bepaal ik de grootte van een object in Python?

Het antwoord, “Gebruik gewoon sys.getsizeof“, is geen volledig antwoord.

Dat antwoord werktdirect voor ingebouwde objecten, maar het houdt geen rekening met wat die objecten kunnen bevatten, met name welke typen, zoals aangepaste objecten, tupels, lijsten, dicts en sets bevatten . Ze kunnen elkaars instanties bevatten, evenals getallen, tekenreeksen en andere objecten.

Een vollediger antwoord

Met behulp van 64-bit Python 3.6 uit de Anaconda-distributie, met sys.getsizeof, heb ik de minimumgrootte van de volgende objecten bepaald, en merk op dat sets en dicts vooraf ruimte toewijzen, zodat lege dat niet doen’ t groeien weer tot na een bepaald bedrag (dat kan variëren per implementatie van de taal):

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.

Hoe interpreteer je dit? Stel je hebt een set met 10 items erin. Als elk item 100 bytes is, hoe groot is dan de hele gegevensstructuur? De set is 736 zelf omdat deze een keer is vergroot tot 736 bytes. Dan voeg je de grootte van de items toe, dus dat is in totaal 1736 bytes

Enkele kanttekeningen bij functie- en klassedefinities:

Merk op dat elke klassedefinitie een proxy __dict__(48 bytes) structuur heeft voor klasseattrs. Elk slot heeft een descriptor (zoals een property) in de klassedefinitie.

Slotted instances beginnen met 48 bytes op hun eerste element, en nemen elk extra toe met 8 bytes. Alleen lege sleufobjecten hebben 16 bytes, en een instantie zonder gegevens heeft weinig zin.

Elke functiedefinitie heeft ook code-objecten, misschien docstrings en andere mogelijke attributen, zelfs een __dict__.

Houd er rekening mee dat we sys.getsizeof()gebruiken omdat we ons zorgen maken over het marginale ruimtegebruik, inclusief de overhead voor het verzamelen van afval voor het object, uit de documenten:

getsizeof()roept de __sizeof__methode van het object aan en voegt een
extra overhead voor vuilnismannen als het object wordt beheerd door de
vuilnisophaler.

Houd er ook rekening mee dat het wijzigen van de grootte van lijsten (bijvoorbeeld door er herhaaldelijk aan toe te voegen) ervoor zorgt dat ze vooraf ruimte toewijzen, net als bij sets en dicts. Van de listobj.c-broncode:

   /* This over-allocates proportional to the list size, making room
     * for additional growth.  The over-allocation is mild, but is
     * enough to give linear-time amortized behavior over a long
     * sequence of appends() in the presence of a poorly-performing
     * system realloc().
     * The growth pattern is:  0, 4, 8, 16, 25, 35, 46, 58, 72, 88, ...
     * Note: new_allocated won't overflow because the largest possible value
     *       is PY_SSIZE_T_MAX * (9 / 8) + 6 which always fits in a size_t.
     */
    new_allocated = (size_t)newsize + (newsize >> 3) + (newsize < 9 ? 3 : 6);

Historische gegevens

Python 2.7-analyse, bevestigd met guppy.hpyen 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.

Houd er rekening mee dat woordenboeken (maar geen sets) heb een meer
compacte weergave
in Python 3.6

Ik denk dat 8 bytes per extra item om naar te verwijzen heel logisch is op een 64-bits machine. Die 8 bytes wijzen naar de plaats in het geheugen waar het item zich bevindt. De 4 bytes hebben een vaste breedte voor unicode in Python 2, als ik het me goed herinner, maar in Python 3 wordt str een unicode met een breedte die gelijk is aan de maximale breedte van de tekens.

En voor meer informatie over slots, zie dit antwoord.

Een completere functie

We willen een functie die de elementen doorzoekt in lijsten, tuples, sets, dicts, obj.__dict__‘s en obj.__slots__, evenals andere dingen waar we misschien nog niet aan hebben gedacht.

We willen vertrouwen op gc.get_referentsom deze zoekopdracht uit te voeren, omdat het op C-niveau werkt (waardoor het erg snel is). Het nadeel is dat get_referents overtollige leden kunnen retourneren, dus we moeten ervoor zorgen dat we niet dubbel tellen.

Klassen, modules en functies zijn singletons – ze bestaan ​​één keer in het geheugen. We zijn niet zo geïnteresseerd in hun grootte, omdat we er niet veel aan kunnen doen – ze maken deel uit van het programma. We tellen ze dus niet als er naar verwezen wordt.

We gaan een zwarte lijst met typen gebruiken, zodat we niet het hele programma in onze matentelling opnemen.

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

Om dit te contrasteren met de volgende whitelist-functie, weten de meeste objecten hoe ze zichzelf moeten doorkruisen voor het verzamelen van afval (wat ongeveer is waar we naar op zoek zijn als we willen weten hoe duur bepaalde objecten in het geheugen zijn. Deze functionaliteit wordt gebruikt door gc.get_referents.) Deze maatregel zal echter veel uitgebreider zijn dan we hadden bedoeld als we niet voorzichtig zijn.

Functies weten bijvoorbeeld heel veel over de modules waarin ze zijn gemaakt.

Een ander punt van contrast is dat strings die sleutels zijn in woordenboeken meestal worden geïnterneerd, zodat ze niet worden gedupliceerd. Door te controleren op id(key)kunnen we ook voorkomen dat we duplicaten tellen, wat we in de volgende sectie doen. De zwarte lijst-oplossing slaat het tellen van sleutels over die helemaal strings zijn.

Typen op de witte lijst, recursieve bezoeker

Om de meeste van deze typen zelf te behandelen, in plaats van te vertrouwen op de gc-module, heb ik deze recursieve functie geschreven om te proberen de grootte van de meeste Python-objecten, inclusief de meeste ingebouwde, typen in de collecties te schatten. module en aangepaste typen (slotted en anderszins).

Dit soort functies geeft veel meer gedetailleerde controle over de typen die we gaan tellen voor geheugengebruik, maar heeft het gevaar dat belangrijke typen worden weggelaten:

import sys
from numbers import Number
from collections import deque
from collections.abc import Set, Mapping
ZERO_DEPTH_BASES = (str, bytes, Number, range, bytearray)
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, 'items'):
            size += sum(inner(k) + inner(v) for k, v in getattr(obj, 'items')())
        # 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)

En ik heb het nogal terloops getest (ik zou het moeten 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

Deze implementatie breekt af op klassedefinities en functiedefinities omdat we niet op al hun attributen letten, maar aangezien ze maar één keer in het geheugen voor het proces zouden moeten bestaan, maakt hun grootte er eigenlijk niet zoveel uit.

p>


Antwoord 3, autoriteit 16%

De asizeof-module van het Pympler-pakket kan dit doen.

Gebruik als volgt:

from pympler import asizeof
asizeof.asizeof(my_object)

In tegenstelling tot sys.getsizeof, werkt het voor je zelfgemaakte objecten. Het werkt zelfs met 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

Als vermeld,

De (byte)codegrootte van objecten zoals klassen, functies, methoden, modules, enz. kan worden opgenomen door optie code=Truein te stellen.

En als je een andere kijk op live data nodig hebt, Pympler’s

module muppywordt gebruikt voor online monitoring van een Python-toepassing
en module Class Trackerbiedt offline analyse van de levensduur van
geselecteerde Python-objecten.


Antwoord 4, autoriteit 11%

Voor numpy-arrays werkt getsizeofniet – voor mij geeft het om de een of andere reden altijd 40 terug:

from pylab import *
from sys import getsizeof
A = rand(10)
B = rand(10000)

Dan (in ipython):

In [64]: getsizeof(A)
Out[64]: 40
In [65]: getsizeof(B)
Out[65]: 40

Gelukkig echter:

In [66]: A.nbytes
Out[66]: 80
In [67]: B.nbytes
Out[67]: 80000

Antwoord 5, autoriteit 5%

U kunt het object serialiseren om een ​​maat af te leiden die nauw verband houdt met de grootte van het object:

import pickle
## let o be the object whose size you want to measure
size_estimate = len(pickle.dumps(o))

Als je objecten wilt meten die niet kunnen worden gebeitst (bijvoorbeeld vanwege lambda-expressies), kan dille of cloudpickle een oplossing zijn.


Antwoord 6, autoriteit 3%

Python 3.8 (Q1 2019) verandert enkele resultaten van sys.getsizeof, zoals hier aangekondigddoor Raymond Hettinger:

Python-containers zijn 8 bytes kleiner bij 64-bits builds.

tuple ()  48 -> 40       
list  []  64 ->56
set()    224 -> 216
dict  {} 240 -> 232

Dit komt na uitgave 33597en Inada Naoki (methane)‘s werk rond Compact PyGC_Head en PR 7043

Dit idee verkleint de PyGC_Head-grootte tot twee woorden.

Momenteel neemt PyGC_Head drie woorden; gc_prev, gc_nexten gc_refcnt.

  • gc_refcntwordt gebruikt bij het verzamelen, voor verwijdering van de proefversie.
  • gc_prevwordt gebruikt voor tracking en untracking.

Dus als we tracking/untracking kunnen vermijden tijdens de proefverwijdering, kunnen gc_preven gc_refcntdezelfde geheugenruimte delen.

Zie commit d5c875b:

Eén Py_ssize_tlid verwijderd uit PyGC_Head.
Alle door GC gevolgde objecten (bijv. tuple, lijst, dict) worden met 4 of 8 bytes verkleind.


Antwoord 7, autoriteit 2%

Gebruik sys.getsizeof()als u dit NIET doet T maten van gekoppelde (geneste) objecten wilt opnemen.

Als u echter subobjecten wilt tellen die zijn genest in lijsten, dicts, sets, tuples – en meestal is DIT wat u zoekt – gebruik dan de recursieve functie deep sizeof()als hieronder weergegeven:

import sys
def sizeof(obj):
    size = sys.getsizeof(obj)
    if isinstance(obj, dict): return size + sum(map(sizeof, obj.keys())) + sum(map(sizeof, obj.values()))
    if isinstance(obj, (list, tuple, set, frozenset)): return size + sum(map(sizeof, obj))
    return size

Je kunt deze functie ook vinden in de handigetoolbox, samen met vele andere handige oneliners:

https://github.com/mwojnars/nifty/blob/master/ util.py


Antwoord 8, autoriteit 2%

Dit kan ingewikkelder zijn dan het lijkt, afhankelijk van hoe je dingen wilt tellen. Als u bijvoorbeeld een lijst met ints heeft, wilt u dan de grootte van de lijst met de verwijzingennaar de ints? (d.w.z. alleen een lijst maken, niet wat erin staat), of wilt u de daadwerkelijke gegevens waarnaar wordt verwezen, opnemen, in welk geval u dubbele verwijzingen moet behandelen, en hoe u dubbeltellingen kunt voorkomen wanneer twee objecten verwijzingen bevatten naar hetzelfde voorwerp.

Misschien wil je eens kijken naar een van de python-geheugenprofilers, zoals pysizerom te zien of ze voldoen aan uw behoeften.


Antwoord 9

Ik ben dit probleem zelf vaak tegengekomen en heb een kleine functie geschreven (geïnspireerd door het antwoord van @aaron-hall) & tests die doen wat ik had verwacht dat sys.getsizeof zou doen:

https://github.com/bosswissam/pysize

Als je geïnteresseerd bent in het achtergrondverhaal, hier is het

EDIT: Bijgevoegd de onderstaande code voor gemakkelijke verwijzing. Bekijk de github-link om de meest actuele code te zien.

   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

Antwoord 10

Hier is een snel script dat ik heb geschreven op basis van de eerdere antwoorden op lijstgroottes van alle variabelen

for i in dir():
    print (i, sys.getsizeof(eval(i)) )

Antwoord 11

Als je niet de exacte grootte van het object nodig hebt, maar ongeveer om te weten hoe groot het is, is een snelle (en vuile) manier om het programma te laten draaien, voor een langere periode te slapen en het geheugen te controleren gebruik (bijv. Mac’s activiteitenmonitor) door dit specifieke python-proces. Dit zou effectief zijn wanneer u de grootte van één enkel groot object in een python-proces probeert te vinden. Zo wilde ik onlangs het geheugengebruik van een nieuwe datastructuur controleren en vergelijken met die van de ingestelde datastructuur van Python. Eerst schreef ik de elementen (woorden uit een groot openbaar domeinboek) naar een set, controleerde toen de grootte van het proces en deed toen hetzelfde met de andere datastructuur. Ik ontdekte dat het Python-proces met een set twee keer zoveel geheugen in beslag neemt als de nieuwe datastructuur. Nogmaals, je zou niet precies kunnen zeggen dat het geheugen dat door het proces wordt gebruikt, gelijk is aan de grootte van het object. Naarmate de grootte van het object groter wordt, komt dit dichterbij omdat het geheugen dat door de rest van het proces wordt verbruikt, verwaarloosbaar wordt in vergelijking met de grootte van het object dat je probeert te bewaken.


Antwoord 12

Ik gebruik deze truc… May zal niet nauwkeurig zijn op kleine objecten, maar ik denk dat het veel nauwkeuriger is voor een complex object (zoals een pygame-oppervlak) dan voor sys.getsizeof()

import pygame as pg
import os
import psutil
import time
process = psutil.Process(os.getpid())
pg.init()    
vocab = ['hello', 'me', 'you', 'she', 'he', 'they', 'we',
         'should', 'why?', 'necessarily', 'do', 'that']
font = pg.font.SysFont("monospace", 100, True)
dct = {}
newMem = process.memory_info().rss  # don't mind this line
Str = f'store ' + f'Nothing \tsurface use about '.expandtabs(15) + \
      f'0\t bytes'.expandtabs(9)  # don't mind this assignment too
usedMem = process.memory_info().rss
for word in vocab:
    dct[word] = font.render(word, True, pg.Color("#000000"))
    time.sleep(0.1)  # wait a moment
    # get total used memory of this script:
    newMem = process.memory_info().rss
    Str = f'store ' + f'{word}\tsurface use about '.expandtabs(15) + \
          f'{newMem - usedMem}\t bytes'.expandtabs(9)
    print(Str)
    usedMem = newMem

Op mijn Windows 10, python 3.7.3, is de uitvoer:

store hello          surface use about 225280    bytes
store me             surface use about 61440     bytes
store you            surface use about 94208     bytes
store she            surface use about 81920     bytes
store he             surface use about 53248     bytes
store they           surface use about 114688    bytes
store we             surface use about 57344     bytes
store should         surface use about 172032    bytes
store why?           surface use about 110592    bytes
store necessarily    surface use about 311296    bytes
store do             surface use about 57344     bytes
store that           surface use about 110592    bytes

Antwoord 13

U kunt getSizeof() gebruiken zoals hieronder vermeld om de grootte van een object te bepalen

import sys
str1 = "one"
int_element=5
print("Memory size of '"+str1+"' = "+str(sys.getsizeof(str1))+ " bytes")
print("Memory size of '"+ str(int_element)+"' = "+str(sys.getsizeof(int_element))+ " bytes")

Antwoord 14

Gebruik de volgende functie om de werkelijke grootte van een python-object te krijgen:

import sys
import gc
def actualsize(input_obj):
    memory_size = 0
    ids = set()
    objects = [input_obj]
    while objects:
        new = []
        for obj in objects:
            if id(obj) not in ids:
                ids.add(id(obj))
                memory_size += sys.getsizeof(obj)
                new.append(obj)
        objects = gc.get_referents(*new)
    return memory_size
actualsize([1, 2, [3, 4, 5, 1]])

Referentie: https://towardsdatascience.com /the-strange-size-of-python-objects-in-memory-ce87bdfbb97f

LEAVE A REPLY

Please enter your comment!
Please enter your name here

4 + 11 =

Other episodes