En C, on peut trouver la taille d'un int
, char
, etc. Je veux savoir comment obtenir la taille d'objets comme une chaîne, un entier, etc. en Python.
Question connexe: Combien d'octets par élément y a-t-il dans une liste Python (Tuple)?
J'utilise un fichier XML contenant des champs de taille spécifiant la taille de la valeur. Je dois analyser ce XML et faire mon codage. Lorsque je souhaite modifier la valeur d'un champ particulier, je vérifie le champ de taille de cette valeur. Ici, je veux comparer si la nouvelle valeur que je dois entrer a la même taille que celle de XML. Je dois vérifier la taille de la nouvelle valeur. Dans le cas d'une chaîne, je peux dire que c'est la longueur. Mais en cas d'int, float, etc. Je suis confus.
Utilisez simplement la fonction sys.getsizeof définie dans le module sys
.
sys.getsizeof(object[, default])
:Renvoie la taille d'un objet en octets. L'objet peut être n'importe quel type d'objet. Tous les objets intégrés renverront des résultats corrects, mais cela ne doit pas nécessairement être vrai pour les extensions tierces, car il s'agit d'une implémentation spécifique.
L'argument
default
permet de définir une valeur qui sera renvoyée si le type d'objet ne permet pas de récupérer la taille et provoquerait unTypeError
.
getsizeof
appelle la méthode__sizeof__
de l'objet et ajoute une surcharge supplémentaire pour le ramasse-miettes si l'objet est géré par le ramasse-miettes.
Exemple d'utilisation, dans 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 vous êtes dans python <2.6 et que vous n'avez pas sys.getsizeof
, vous pouvez utiliser ce module complet . Jamais utilisé cependant.
Comment déterminer la taille d'un objet en Python?
La réponse "Il suffit d'utiliser sys.getsizeof" n'est pas une réponse complète.
Cette réponse fonctionne directement pour les objets intégrés, mais elle ne tient pas compte de ce que ces objets peuvent contenir, en particulier quels types, tels que les objets personnalisés, les n-uplets contient des listes, des dicts et des ensembles. Ils peuvent contenir des instances les unes des autres, ainsi que des nombres, des chaînes et d'autres objets.
En utilisant _ Python64 bits 64 bits de la distribution Anaconda, avec sys.getsizeof, j'ai déterminé la taille minimale des objets suivants et noté que les ensembles et les blocs préalloués afin que les objets vides ne croissent plus après un montant fixe (qui peut varier en fonction de la mise en œuvre de la langue):
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.
Comment interprétez-vous cela? Disons que vous avez un ensemble contenant 10 éléments. Si chaque élément a 100 octets, quelle est la taille de la structure de données? L'ensemble est 736 lui-même car il a été dimensionné une fois à 736 octets. Ensuite, vous ajoutez la taille des éléments, de sorte que 1736 octets au total
Quelques mises en garde concernant les définitions de fonction et de classe:
Notez que chaque définition de classe a une structure de proxy __dict__
(48 octets) pour la classe attrs. Chaque emplacement a un descripteur (comme un property
) dans la définition de la classe.
Les instances placées commencent par 48 octets sur leur premier élément et augmentent de 8 chaque élément supplémentaire. Seuls les objets vides vides ont 16 octets et une instance sans données n'a pas beaucoup de sens.
De plus, chaque définition de fonction a des objets de code, peut-être des docstrings, et d'autres attributs possibles, même un __dict__
.
Analyse Python 2.7, confirmée avec guppy.hpy
et 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.
Notez que les dictionnaires ( mais pas les ensembles ) ont un représentation plus compacte dans Python 3.6
Je pense que 8 octets par élément supplémentaire à référencer a beaucoup de sens sur une machine 64 bits. Ces 8 octets indiquent l'emplacement en mémoire de l'élément contenu. Les 4 octets ont une largeur fixe pour unicode dans Python 2, si je me souviens bien, mais dans Python 3, str devient un unicode de largeur égale à la largeur maximale des caractères.
(Et pour plus sur les créneaux horaires, voir cette réponse )
Nous voulons une fonction qui recherche les éléments dans les listes, les tuples, les ensembles, les dicts, les obj.__dict__
et les obj.__slots__
, ainsi que dans d’autres choses auxquelles nous n’avions peut-être pas encore pensé.
Nous voulons nous appuyer sur gc.get_referents
pour effectuer cette recherche, car elle fonctionne au niveau C (ce qui la rend très rapide). L'inconvénient est que get_referents peut renvoyer des membres redondants, nous devons donc nous assurer de ne pas compter en double.
Les classes, les modules et les fonctions sont des singletons - ils existent une fois en mémoire. Nous ne sommes pas tellement intéressés par leur taille, car nous ne pouvons rien faire à leur sujet - ils font partie du programme. Nous éviterons donc de les compter s’il se trouve qu’ils sont référencés.
Nous allons utiliser une liste noire de types afin de ne pas inclure le programme entier dans notre nombre de tailles.
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
Pour mettre cela en contraste avec la fonction de liste blanche suivante, la plupart des objets savent comment se traverser eux-mêmes à des fins de récupération de place (ce qui est à peu près ce que nous recherchons lorsque nous voulons savoir combien coûtent certains objets en mémoire. Cette fonctionnalité est utilisée par gc.get_referents
.) Cependant, cette mesure aura une portée beaucoup plus large que ce que nous avions prévu si nous ne faisons pas attention.
Par exemple, les fonctions en savent beaucoup sur les modules dans lesquels elles ont été créées.
Un autre point de contraste est que les chaînes qui sont des clés dans les dictionnaires sont généralement internées afin qu'elles ne soient pas dupliquées. Vérifier id(key)
nous permettra également d’éviter le comptage des doublons, ce que nous faisons dans la section suivante. La solution de la liste noire ignore le comptage des clés qui sont des chaînes.
Pour couvrir la plupart de ces types moi-même, au lieu de me fier au module gc, j’ai écrit cette fonction récursive pour tenter d’estimer la taille de la plupart des objets Python, y compris la plupart des fonctions intégrées, des types du module collections et des éléments personnalisés. types (fendus et autres).
Ce type de fonction offre un contrôle beaucoup plus fin sur les types que nous allons compter pour l'utilisation de la mémoire, mais présente le danger de laisser des types en dehors:
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)
Et je l'ai testé plutôt avec désinvolture (je devrais unittest):
>>> 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
Cette implémentation se décompose sur les définitions de classe et les définitions de fonction car nous ne cherchons pas tous leurs attributs, mais comme ils ne doivent exister qu'une seule fois en mémoire pour le processus, leur taille importe peu.
Pour les tableaux numpy, getsizeof
ne fonctionne pas - pour moi, il retourne toujours 40 pour une raison quelconque:
from pylab import *
from sys import getsizeof
A = Rand(10)
B = Rand(10000)
Puis (en ipython):
In [64]: getsizeof(A)
Out[64]: 40
In [65]: getsizeof(B)
Out[65]: 40
Heureusement, cependant:
In [66]: A.nbytes
Out[66]: 80
In [67]: B.nbytes
Out[67]: 80000
Le module asizeof
du package Pympler peut le faire.
Utilisez comme suit:
_from pympler import asizeof
asizeof.asizeof(my_object)
_
Contrairement à _sys.getsizeof
_, il fonctionne pour vos objets créés par vous-même . Cela fonctionne même avec 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
_
Comme mentionné ,
Et si vous avez besoin d'une autre vue sur les données en direct, Pympler
le module
muppy
est utilisé pour la surveillance en ligne d'une application Python et du moduleClass Tracker
fournit une analyse hors ligne de la durée de vie de la sélection Python objets.
Cela peut être plus compliqué qu'il n'y paraît, selon la manière dont vous voulez compter les choses. Par exemple, si vous avez une liste d'ints, voulez-vous la taille de la liste contenant les références aux ints? (c’est-à-dire une liste uniquement, pas ce qui y est contenu) ou voulez-vous inclure les données réelles pointées, auquel cas vous devez traiter les références en double et comment éviter le double comptage lorsque deux objets contiennent des références à le même objet.
Vous voudrez peut-être consulter l'un des python profileurs de mémoire, tels que pysizer , pour voir s'ils répondent à vos besoins.
Ayant moi-même rencontré ce problème plusieurs fois, j'ai écrit une petite fonction (inspirée de la réponse de @ aaron-hall) et des tests qui font ce à quoi je m'attendais de la part de sys.getsizeof:
https://github.com/bosswissam/pysize
Si l'histoire vous intéresse, le voici
EDIT: Joindre le code ci-dessous pour faciliter la consultation. Pour voir le code le plus récent, veuillez consulter le lien 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
Voici un script rapide que j'ai écrit sur la base des réponses précédentes pour lister les tailles de toutes les variables
for i in dir():
print (i, sys.getsizeof(eval(i)) )
Python 3.8 (T1 2019) modifiera certains des résultats de sys.getsizeof
, comme annoncé ici par Raymond Hettinger:
Les conteneurs Python sont plus petits de 8 octets sur les versions 64 bits.
_Tuple () 48 -> 40
list [] 64 ->56
set() 224 -> 216
dict {} 240 -> 232
_
Cela vient après numéro 33597 et Inada Naoki (methane
) autour de Compact PyGC_Head, et PR 704
Cette idée réduit la taille de PyGC_Head à deux mots .
Actuellement, PyGC_Head prend trois mots ; _
gc_prev
_, _gc_next
_ et _gc_refcnt
_.
- _
gc_refcnt
_ est utilisé lors de la collecte, pour la suppression d'essai.- _
gc_prev
_ est utilisé pour le suivi et le retrait du suivi.Par conséquent, si nous pouvons éviter de suivre/supprimer le suivi lors de la suppression de la version d'évaluation, _
gc_prev
_ et _gc_refcnt
_ peuvent partager le même espace mémoire.
Voir commit d5c875b :
Suppression d'un membre _
Py_ssize_t
_ de _PyGC_Head
_.
La taille de tous les objets suivis par le GC (exemple: tuple, liste, dict) est réduite de 4 ou 8 octets.
Si vous n’avez pas besoin de la taille exacte de l’objet mais de la taille de celui-ci, une solution rapide (et sale) est de laisser le programme s'exécuter, dormir pendant une période prolongée et vérifier l'utilisation de la mémoire : Moniteur d’activité de Mac) par ce processus python particulier. Cela serait efficace lorsque vous essayez de trouver la taille d'un seul objet volumineux dans un processus python. Par exemple, je voulais récemment vérifier l'utilisation de la mémoire d'une nouvelle structure de données et la comparer à celle de la structure de données définie de Python. J'ai d'abord écrit les éléments (mots d'un grand livre du domaine public) dans un ensemble, puis vérifié la taille du processus, puis fait la même chose avec l'autre structure de données. J'ai découvert que le processus Python avec un jeu prend deux fois plus de mémoire que la nouvelle structure de données. Encore une fois, vous ne pourriez pas dire exactement que la mémoire utilisée par le processus est égale à la taille de l'objet. Au fur et à mesure que la taille de l'objet grossit, la taille de la mémoire consommée par le reste du processus devient négligeable par rapport à la taille de l'objet que vous essayez de surveiller.