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

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

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 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 un medio 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é.

¿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, tales como objetos personalizados, 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 tengo en cuenta que los conjuntos y los dados preallocan el espacio 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 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. 

como interpretas esto? Bueno, digamos que tienes un set con 10 elementos. Si cada elemento es de 100 bytes cada uno, ¿qué tan grande es la estructura de datos completa? 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 1736 bytes en total.

Algunas advertencias para las definiciones de funciones y clases:

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 una adicional. Solo los objetos ranurados 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 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. 

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 )

Una función más completa

Queremos una función que busque los elementos en listas, tuplas, conjuntos, dados, obj.__dict__ ‘s y obj.__slots__ , así como otras cosas que aún no hayamos pensado.

Queremos confiar en gc.get_referents para hacer esta búsqueda porque funciona en el nivel C (por lo que es muy rápido). El inconveniente es que get_referents puede devolver miembros redundantes, por lo que debemos asegurarnos de no contar dos veces.

Las clases, los módulos y las funciones son singletons, existen una vez en la memoria. No estamos tan interesados ​​en su tamaño, ya que no hay mucho que podamos hacer al respecto, son parte del progtwig. Así que evitaremos contarlos si son referenciados.

Vamos a utilizar una lista negra de tipos para no incluir todo el progtwig en nuestro recuento de tamaños.

 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 

Para contrastar esto con la siguiente función de la lista blanca, la mayoría de los objetos saben cómo atravesarse a sí mismos para la recolección de elementos no utilizados (que es aproximadamente lo que estamos buscando cuando queremos saber qué costos tienen en la memoria ciertos objetos. Esta funcionalidad es utilizada por gc.get_referents .) Sin embargo, esta medida tendrá un scope mucho más amplio de lo que pretendíamos si no tenemos cuidado.

Por ejemplo, las funciones saben bastante acerca de los módulos en los que se crean.

Otro punto de contraste es que las cadenas que son claves en los diccionarios generalmente se internan para que no se dupliquen. La verificación de la id(key) también nos permitirá evitar contar duplicados, lo que hacemos en la siguiente sección. La solución de la lista negra omite las claves de conteo que son cadenas en total.

Tipos en lista blanca, visitante recursivo (implementación antigua)

Para cubrir la mayoría de estos tipos, en lugar de confiar en el módulo gc, 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 (con ranuras y otros) .

Este tipo de función proporciona un control mucho más preciso sobre los tipos que vamos a contar para el uso de la memoria, pero tiene el peligro de dejar de lado los tipos:

 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 

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

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 

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 menciono

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 de Python y el módulo Class Tracker proporciona un análisis fuera de línea de la vida útil de los objetos de Python seleccionados.

Esto puede ser más complicado de lo que parece, dependiendo de cómo quiera contar las cosas. Por ejemplo, si tiene una lista de entradas, ¿desea el tamaño de la lista que contiene las referencias a las entradas? (es decir, solo la lista, no lo que contiene), o si desea incluir los datos reales señalados, en cuyo caso debe tratar con referencias duplicadas, y cómo evitar el recuento doble cuando dos objetos contienen referencias a el mismo objeto

Es posible que desee echar un vistazo a uno de los perfiladores de memoria de Python, como pysizer para ver si satisfacen sus necesidades.

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

Después de haberme encontrado con 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 sys.getsizeof hiciera:

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 

Python 3.8 (Q1 2019) cambiará algunos de los resultados de sys.getsizeof , como lo anunció aquí Raymond Hettinger:

Los contenedores de Python son 8 bytes más pequeños en las versiones de 64 bits.

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

Esto viene después del número 33597 y el trabajo de Inada Naoki ( methane ) en torno a Compact PyGC_Head y PR 7043

Esta idea reduce el tamaño de PyGC_Head a dos palabras .

Actualmente, PyGC_Head toma tres palabras ; gc_prev , gc_next y gc_refcnt .

  • gc_refcnt se utiliza al recostackr, para la eliminación de la gc_refcnt de prueba.
  • gc_prev se utiliza para rastrear y des-rastrear.

Entonces, si podemos evitar el seguimiento / no seguimiento mientras se elimina la versión de prueba, gc_prev y gc_refcnt pueden compartir el mismo espacio de memoria.

Ver commit d5c875b :

Se eliminó un miembro PyGC_Head de PyGC_Head .
El tamaño de todos los objetos rastreados por GC (por ejemplo, tupla, lista, dict) se reduce en 4 u 8 bytes.

Si no necesita el tamaño exacto del objeto, pero aproximadamente para saber qué tan grande es, una forma rápida (y sucia) es dejar que el progtwig se ejecute, dormir durante un período prolongado de tiempo y verificar el uso de la memoria (por ejemplo, : Monitor de actividad de Mac) por este particular proceso de python. Esto sería efectivo cuando intenta encontrar el tamaño de un solo objeto grande en un proceso de python. Por ejemplo, recientemente quise comprobar el uso de la memoria de una nueva estructura de datos y compararla con la estructura de datos establecida de Python. Primero escribí los elementos (palabras de un gran libro de dominio público) en un conjunto, luego verifiqué el tamaño del proceso y luego hice lo mismo con la otra estructura de datos. Descubrí que el proceso de Python con un conjunto requiere el doble de memoria que la nueva estructura de datos. Nuevamente, no podría decir exactamente que la memoria utilizada por el proceso es igual al tamaño del objeto. A medida que aumenta el tamaño del objeto, este se acerca cuando la memoria consumida por el rest del proceso se vuelve insignificante en comparación con el tamaño del objeto que está tratando de monitorear.

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