object size - ¿Cómo puedo determinar el tamaño de un objeto en Python?




of an (8)

En C, podemos encontrar el tamaño de un int , char , etc. Quiero saber cómo obtener el tamaño de objetos como una cadena, un entero, etc. en Python.

Pregunta relacionada: ¿Cuántos bytes por elemento hay en una lista de Python (tupla)?

Estoy usando un archivo XML que contiene campos de tamaño que especifican el tamaño del valor. Debo analizar este XML y hacer mi codificación. Cuando quiero cambiar el valor de un campo en particular, verificaré el campo de tamaño de ese valor. Aquí quiero comparar si el nuevo valor que debo ingresar es del mismo tamaño que en XML. Necesito comprobar el tamaño del nuevo valor. En caso de una cadena puedo decir que es la longitud. Pero en caso de int, flotar, etc. estoy confundido.


Answers

Aquí hay un guión rápido que escribí basado en las respuestas anteriores a los tamaños de lista de todas las variables

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

El módulo asizeof del paquete asizeof puede hacer esto.

Use de la siguiente manera:

from pympler import asizeof
asizeof.asizeof(my_object)

A diferencia de sys.getsizeof , funciona para tus objetos de creación propia . Incluso funciona 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

Como se mentioned ,

El tamaño del código (byte) de objetos como clases, funciones, métodos, módulos, etc. se puede incluir configurando el code=True opción code=True .

Y si necesita otra vista sobre datos en vivo, Pympler es

el módulo muppy se utiliza para la supervisión en línea de una aplicación Python y el módulo Class Tracker proporciona un análisis fuera de línea de la vida útil de los objetos Python seleccionados.


Después de haber encontrado este problema muchas veces, escribí una pequeña función (inspirada en la respuesta de @ aaron-hall) y pruebas que hacen lo que habría esperado que hiciera sys.getsizeof:

https://github.com/bosswissam/pysize

Si estás interesado en la historia de fondo, aquí está

EDITAR: Adjuntar el código de abajo para una fácil referencia. Para ver el código más actualizado, verifique el enlace de 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

Simplemente use la función sys.getsizeof definida en el módulo sys .

sys.getsizeof(object[, default]) :

Devuelve el tamaño de un objeto en bytes. El objeto puede ser cualquier tipo de objeto. Todos los objetos incorporados devolverán los resultados correctos, pero esto no tiene por qué ser cierto para las extensiones de terceros, ya que es específico de la implementación.

El argumento default permite definir un valor que se devolverá si el tipo de objeto no proporciona los medios para recuperar el tamaño y causaría un TypeError .

getsizeof llama al método __sizeof__ del objeto y agrega una sobrecarga adicional al recolector de basura si el objeto es administrado por el recolector de basura.

Ejemplo de uso, en 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

Si está en Python <2.6 y no tiene sys.getsizeof , puede usar este módulo extenso en su lugar. Aunque nunca lo usé.


Para matrices numpy, getsizeof no funciona, para mí siempre devuelve 40 por alguna razón:

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

Entonces (en ipython):

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

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

Afortunadamente, sin embargo:

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

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

Primero: una respuesta.

import sys

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

Discusión:
En Python, nunca se puede acceder a direcciones de memoria "directas". ¿Por qué, entonces, necesitaría o querría saber cuántas direcciones están ocupadas por un objeto determinado? Es una pregunta que es completamente inapropiada en ese nivel de abstracción. Cuando estás pintando tu casa, no preguntas qué frecuencias de luz son absorbidas o reflejadas por cada uno de los átomos que forman parte de la pintura, solo preguntas qué color es: los detalles de las características físicas que crean ese color. están al lado del punto. De manera similar, el número de bytes de memoria que ocupa un objeto de Python dado está al lado del punto.

Entonces, ¿por qué estás tratando de usar Python para escribir código C? :)


¿Cómo puedo determinar el tamaño de un objeto en Python?

La respuesta, "Sólo use sys.getsizeof" no es una respuesta completa.

Esa respuesta funciona para los objetos incorporados directamente, pero no tiene en cuenta lo que esos objetos pueden contener, específicamente, qué tipos, como tuplas, listas, dados y conjuntos contienen. Pueden contener instancias entre sí, así como números, cadenas y otros objetos.

Una respuesta más completa

Al usar Python 3.6 de 64 bits de la distribución de Anaconda, con sys.getsizeof, he determinado el tamaño mínimo de los siguientes objetos y he notado que los conjuntos y los dicts asignan un espacio preasignado para que los vacíos no vuelvan a crecer hasta después de una cantidad establecida (que puede variar según la implementación del lenguaje):

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.

como interpretas esto? Bueno, digamos que tienes un set con 10 elementos. Si cada elemento tiene 100 bytes cada uno, ¿cuál es el tamaño de toda la estructura de datos? El conjunto es el mismo 736 porque se ha dimensionado una vez hasta 736 bytes. Luego agrega el tamaño de los elementos, lo que equivale a un total de 1736 bytes.

Algunas advertencias para la función y definiciones de clase:

Tenga en cuenta que cada definición de clase tiene una __dict__ proxy __dict__ (48 bytes) para los atributos de clase. Cada ranura tiene un descriptor (como una property ) en la definición de clase.

Las instancias ranuradas comienzan con 48 bytes en su primer elemento, y aumentan en 8 cada uno adicional. Sólo los objetos con espacios vacíos tienen 16 bytes, y una instancia sin datos tiene muy poco sentido.

Además, cada definición de función tiene objetos de código, tal vez cadenas de documentación y otros atributos posibles, incluso un __dict__ .

Análisis de Python 2.7, confirmado con guppy.hpy y 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.

Tenga en cuenta que los diccionarios ( pero no los conjuntos ) obtuvieron una representación más compacta en Python 3.6

Creo que 8 bytes por elemento adicional a referencia tienen mucho sentido en una máquina de 64 bits. Esos 8 bytes apuntan al lugar en la memoria donde se encuentra el elemento. Los 4 bytes tienen un ancho fijo para Unicode en Python 2, si recuerdo bien, pero en Python 3, str se convierte en un Unicode de ancho igual al ancho máximo de los caracteres.

(Y para más información sobre las tragamonedas, vea esta respuesta )

Visitante recursivo para una función más completa

Para cubrir la mayoría de estos tipos, escribí esta función recursiva para tratar de estimar el tamaño de la mayoría de los objetos de Python, incluidos la mayoría de los incorporados, los tipos en el módulo de colecciones y los tipos personalizados (ranurados y de otro tipo):

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)

Y lo probé de forma casual (debería probarlo):

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

Se descompone en las definiciones de clase y en la función porque no busco todos sus atributos, pero como solo deben existir una vez en la memoria para el proceso, su tamaño realmente no importa demasiado.


Un uso para las metaclases es agregar nuevas propiedades y métodos a una instancia automáticamente.

Por ejemplo, si nos fijamos en los modelos Django , su definición parece un poco confusa. Parece como si solo estuvieras definiendo propiedades de clase:

class Person(models.Model):
    first_name = models.CharField(max_length=30)
    last_name = models.CharField(max_length=30)

Sin embargo, en tiempo de ejecución, los objetos Person se llenan con todo tipo de métodos útiles. Vea la source de algunos metaclassery asombrosos.







python object memory memory-management sizeof