str Come posso determinare la dimensione di un oggetto in Python?




python metodo__ str__ (7)

Come posso determinare la dimensione di un oggetto in Python?

La risposta, "Usa semplicemente sys.getsizeof" non è una risposta completa.

Quella risposta funziona direttamente per gli oggetti incorporati, ma non tiene conto di ciò che questi oggetti possono contenere, in particolare, quali tipi, come tuple, elenchi, dadi e set contengono. Possono contenere istanze l'un l'altro, oltre a numeri, stringhe e altri oggetti.

Una risposta più completa

Usando Python 3.6 a 64 bit dalla distribuzione di Anaconda, con sys.getsizeof, ho determinato la dimensione minima dei seguenti oggetti, e noto che imposta e detta prealloca lo spazio in modo che quelli vuoti non ricrescano fino a dopo un determinato importo (che può varia in base all'implementazione della lingua):

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    doesn't 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.

Come interpreti questo? Beh, dì che hai un set con 10 oggetti. Se ogni elemento è di 100 byte ciascuno, quanto è grande l'intera struttura dati? Il set è 736 stesso perché ha raggiunto una dimensione fino a 736 byte. Quindi aggiungi la dimensione degli elementi, quindi in totale sono 1736 byte

Alcuni avvertimenti per le definizioni di funzioni e classi:

Nota che ogni definizione di classe ha una struttura proxy __dict__ (48 byte) per la classe attrs. Ogni slot ha un descrittore (come una property ) nella definizione della classe.

Le istanze con intaglio iniziano con 48 byte sul loro primo elemento e aumentano di 8 ogni ulteriore. Solo gli oggetti con slots vuoti hanno 16 byte e un'istanza senza dati ha molto senso.

Inoltre, ogni definizione di funzione contiene oggetti codice, forse docstring e altri possibili attributi, anche un __dict__ .

Analisi Python 2.7, confermata con guppy.hpy e 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    doesn't 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.

Nota che i dizionari ( ma non i set ) hanno una rappresentazione più compatta in Python 3.6

Penso che 8 byte per ogni elemento aggiuntivo da usare abbiano molto senso su una macchina a 64 bit. Questi 8 byte indicano il punto in cui si trova l'elemento contenuto. I 4 byte hanno una larghezza fissa per unicode in Python 2, se ricordo male, ma in Python 3, str diventa un unicode di larghezza uguale alla larghezza massima dei caratteri.

(E per ulteriori informazioni sugli slot, vedi questa risposta )

Visitatore ricorsivo per una funzione più completa

Per coprire la maggior parte di questi tipi, ho scritto questa funzione ricorsiva per cercare di stimare la dimensione della maggior parte degli oggetti Python, compresi la maggior parte dei builtin, i tipi nel modulo delle collezioni e i tipi personalizzati (a fessura e in altro modo):

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)

E l'ho provato in modo piuttosto casuale (dovrei sbloccarlo):

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

È una specie di suddivisione in definizioni di classi e definizioni di funzioni perché non seguo tutti i loro attributi, ma dal momento che dovrebbero esistere solo una volta in memoria per il processo, le loro dimensioni non contano molto.

In C, possiamo trovare la dimensione di un int , char , ecc. Voglio sapere come ottenere dimensioni di oggetti come una stringa, un intero, ecc. In Python.

Domanda correlata: quanti byte per elemento ci sono in una lista Python (tupla)?

Sto usando un file XML che contiene campi di dimensioni che specificano la dimensione del valore. Devo analizzare questo XML e fare la mia codifica. Quando voglio cambiare il valore di un campo particolare, controllerò il campo delle dimensioni di quel valore. Qui voglio confrontare se il nuovo valore che sto per entrare ha le stesse dimensioni di XML. Devo controllare la dimensione del nuovo valore. In caso di stringa posso dire che è la lunghezza. Ma in caso di int, float, ecc. Sono confuso.


Questo può essere più complicato di quanto sembri a seconda di come vuoi contare le cose. Ad esempio, se hai una lista di interi, vuoi la dimensione della lista che contiene i riferimenti agli ints? (ad esempio, elenca solo, non ciò che è contenuto in esso), o vuoi includere i dati reali puntati a, nel qual caso devi occuparti di riferimenti duplicati e come evitare il doppio conteggio quando due oggetti contengono riferimenti a lo stesso oggetto.

Si consiglia di dare un'occhiata a uno dei profiler di memoria Python, come ad esempio pysizer per vedere se soddisfano le vostre esigenze.


Primo: una risposta.

import sys

try: print sys.getsizeof(object)
except AttributeError:
    print "sys.getsizeof exists in Python ≥2.6"

Discussione:
In Python, non puoi mai accedere agli indirizzi di memoria "diretti". Perché, quindi, avresti bisogno o vuoi sapere quanti di questi indirizzi sono occupati da un dato oggetto ?? È una domanda completamente inappropriata a quel livello di astrazione. Quando dipingi la tua casa, non chiedi quali frequenze della luce sono assorbite o riflesse da ciascuno degli atomi costituenti all'interno della pittura, ti chiedi solo di che colore è - i dettagli delle caratteristiche fisiche che creano quel colore sono accanto al punto. Allo stesso modo, il numero di byte di memoria occupati da un dato oggetto Python si trova accanto al punto.

Quindi, perché stai cercando di usare Python per scrivere il codice C? :)


Avendo incontrato questo problema molte volte io stesso, ho scritto una piccola funzione (ispirata alla risposta di @ aaron-hall) e test che fa quello che mi sarei aspettato da sys.getsizeof per fare:

https://github.com/bosswissam/pysize

Se ti interessa il retroscena, eccolo qui

MODIFICA: allegando il codice qui sotto per una facile consultazione. Per vedere il codice più aggiornato, controlla il link github.

    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

Per gli array numpy, getsizeof non funziona - per me restituisce sempre 40 per qualche motivo:

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

Quindi (in ipython):

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

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

Fortunatamente, però:

In [66]: A.nbytes
Out[66]: 80

In [67]: B.nbytes
Out[67]: 80000

Basta usare la funzione sys.getsizeof definita nel modulo sys .

sys.getsizeof(object[, default]) :

Restituisce la dimensione di un oggetto in byte. L'oggetto può essere qualsiasi tipo di oggetto. Tutti gli oggetti incorporati restituiscono risultati corretti, ma questo non deve essere vero per le estensioni di terze parti in quanto è specifica dell'implementazione.

L'argomento default consente di definire un valore che verrà restituito se il tipo di oggetto non fornisce i mezzi per recuperare la dimensione e causerebbe un TypeError .

getsizeof chiama il metodo __sizeof__ dell'oggetto e aggiunge un overhead del garbage collector aggiuntivo se l'oggetto è gestito dal garbage collector.

Esempio di utilizzo, 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

Se sei in Python <2.6 e non hai sys.getsizeof puoi usare invece questo modulo esteso . Non l'ho mai usato però.


Il modulo asizeof del pacchetto asizeof può farlo.

Utilizzare come segue:

from pympler import asizeof
asizeof.asizeof(my_object)

A differenza di sys.getsizeof , funziona per i tuoi oggetti auto-creati . Funziona anche con 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

Come mentioned ,

La dimensione del codice (byte) di oggetti come classi, funzioni, metodi, moduli, ecc. Può essere inclusa impostando il code=True opzione code=True .

E se hai bisogno di un'altra vista sui dati live, Pympler's

modulo muppy è usato per il monitoraggio on-line di un'applicazione Python e modulo Class Tracker fornisce l'analisi off-line della vita degli oggetti Python selezionati.







sizeof