Comment déterminer la taille d'un objet en Python?

685

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 qui contient des champs de taille qui spécifient 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 vais entrer est de la même taille qu'en XML. Je dois vérifier la taille de la nouvelle valeur. Dans le cas d'une chaîne, je peux dire sa longueur. Mais en cas d'int, float, etc. je suis confus.

user46646
la source

Réponses:

665

Utilisez simplement la fonction sys.getsizeof définie dans le sysmodule.

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 est spécifique à l'implémentation.

L' defaultargument permet de définir une valeur qui sera retournée si le type d'objet ne fournit pas de moyen pour récupérer la taille et provoquerait a TypeError.

getsizeofappelle la __sizeof__méthode de l'objet et ajoute une surcharge supplémentaire au garbage collector si l'objet est géré par le garbage collector.

Exemple d'utilisation, 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 vous êtes en python <2.6 et que vous ne l'avez pas, sys.getsizeofvous pouvez utiliser ce module complet à la place. Je ne l'ai jamais utilisé cependant.

nosklo
la source
182
Veuillez ajouter à la clause de non-responsabilité qu'elle ne sera pas vraie pour les objets imbriqués ou les dict imbriqués ou les dict dans les listes, etc.
JohnnyM
8
@ChaimG c'est parce que chaque objet utilise seulement 32 octets !! Les autres sont des références à d'autres objets. Si vous souhaitez tenir compte des objets référencés, vous devez définir une __sizeof__méthode pour votre classe. La dictclasse python intégrée le définit, c'est pourquoi vous obtenez le résultat correct lorsque vous utilisez un objet de type dict.
nosklo
19
La clause de non-responsabilité et les exceptions à ce fonctionnement couvrent presque tous les cas d'utilisation, ce qui rend la getsizeoffonction de peu de valeur prête à l' emploi .
Robino
7
pourquoi l'entier 2 est-il stocké dans 24 octets?
Saher Ahwal
4
@SaherAhwal ce n'est pas seulement un entier, mais un objet complet avec des méthodes, des attributs, des adresses ...
nosklo
372

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 fait le travail pour les objets directement builtin, mais il ne tient pas compte de ce que ces objets peuvent contenir, en particulier, quels types, tels que des objets personnalisés, tuples, listes, dicts et ensembles contiennent. Ils peuvent contenir des instances entre elles, ainsi que des nombres, des chaînes et d'autres objets.

Une réponse plus complète

À l'aide de Python 3.6 64 bits de la distribution Anaconda, avec sys.getsizeof, j'ai déterminé la taille minimale des objets suivants, et notez que les ensembles et les images préallouent de sorte que les espaces vides ne se développent plus qu'après une quantité définie (ce qui peut varient selon 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 de 10 articles. Si chaque élément fait 100 octets chacun, quelle est la taille de la structure de données entière? L'ensemble est 736 lui-même car il a dimensionné une fois à 736 octets. Ensuite, vous ajoutez la taille des éléments, ce qui fait 1736 octets au total

Quelques mises en garde pour les définitions de fonction et de classe:

Notez que chaque définition de classe a une __dict__structure proxy (48 octets) pour les attr de classe. Chaque emplacement a un descripteur (comme a property) dans la définition de classe.

Les instances fendues commencent avec 48 octets sur leur premier élément et augmentent de 8 chacune supplémentaires. Seuls les objets fendus vides ont 16 octets, et une instance sans données n'a que très peu 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 a __dict__.

Notez également que nous utilisons sys.getsizeof()parce que nous nous soucions de l'utilisation de l'espace marginal, qui inclut la surcharge de la récupération de place pour l'objet, à partir des documents :

getsizeof () appelle la __sizeof__méthode de l'objet et ajoute une surcharge supplémentaire au garbage collector si l'objet est géré par le garbage collector.

Notez également que le redimensionnement des listes (par exemple en les ajoutant de manière répétitive) les oblige à préallouer de l'espace, de la même manière que les ensembles et les dict. À partir du code source listobj.c :

    /* 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);

Données historiques

Analyse Python 2.7, confirmée par guppy.hpyet 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 une représentation plus compacte en Python 3.6

Je pense que 8 octets par élément supplémentaire à référencer ont beaucoup de sens sur une machine 64 bits. Ces 8 octets indiquent l'endroit en mémoire où se trouve l'élément contenu. Les 4 octets sont à largeur fixe pour unicode en Python 2, si je me souviens bien, mais en Python 3, str devient un unicode de largeur égale à la largeur maximale des caractères.

(Et pour en savoir plus sur les machines à sous, voir cette réponse )

Une fonction plus complète

Nous voulons une fonction qui recherche les éléments dans les listes, les tuples, les ensembles, les dict, obj.__dict__les, et obj.__slots__, ainsi que d'autres choses auxquelles nous n'avons peut-être pas encore pensé.

Nous voulons nous fier gc.get_referentsà 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 deux fois.

Les classes, modules et fonctions sont des singletons - ils existent une fois dans la mémoire. Nous ne sommes pas tellement intéressés par leur taille, car nous ne pouvons pas faire grand-chose à leur sujet - ils font partie du programme. Nous éviterons donc de les compter s'ils sont référencés.

Nous allons utiliser une liste noire de types afin de ne pas inclure l'intégralité du programme 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 contraster cela avec la fonction mise en liste blanche suivante, la plupart des objets savent comment se déplacer eux-mêmes à des fins de récupération de place (ce qui est approximativement ce que nous recherchons lorsque nous voulons savoir combien certains objets sont chers en mémoire. Cette fonctionnalité est utilisée par gc.get_referents.) Cependant, cette mesure va être beaucoup plus étendue que nous l’avions prévu si nous ne faisons pas attention.

Par exemple, les fonctions en savent beaucoup sur les modules dans lesquels elles sont créées.

Un autre point de contraste est que les chaînes qui sont des clés dans les dictionnaires sont généralement internes afin qu'elles ne soient pas dupliquées. La vérification id(key)nous permettra également d'éviter de compter les doublons, ce que nous faisons dans la section suivante. La solution de liste noire ignore le comptage des clés qui sont des chaînes.

Types sur liste blanche, visiteur récursif (ancienne implémentation)

Pour couvrir la plupart de ces types moi-même, au lieu de compter sur le module gc, j'ai écrit cette fonction récursive pour essayer d'estimer la taille de la plupart des objets Python, y compris la plupart des buildins, des types dans le module collections et des types personnalisés (à fentes et autres) .

Ce type de fonction donne un contrôle beaucoup plus fin sur les types que nous comptons pour l'utilisation de la mémoire, mais a le danger de laisser de côté les types:

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é de façon plutôt nonchalante (je devrais ne pas le tester):

>>> 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 décompose les définitions de classe et les définitions de fonctions parce que nous ne recherchons pas tous leurs attributs, mais comme ils ne devraient exister qu'une seule fois en mémoire pour le processus, leur taille n'a vraiment pas trop d'importance.

Aaron Hall
la source
5
Vous pourriez ajouter que cette réponse est spécifique à CPython (ce qui est implicite si vous obtenez Python via Anaconda)
gerrit
1
CPython est l'implémentation de référence, et je viens de passer en revue les documents en ligne de jython qui fournissent la même API, donc je pense que cela fonctionnera sur d'autres implémentations, tant qu'ils implémentent les API.
Aaron Hall
pour moi n'a pas fonctionné pour les tableaux numpy masqués et non masqués stackoverflow.com/q/58675479/2132157
GM
96

Le module du package Pymplerasizeof peut le faire.

Utilisez comme suit:

from pympler import asizeof
asizeof.asizeof(my_object)

Contrairement à sys.getsizeofcela, cela fonctionne pour vos objets auto-créés . 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é ,

La taille du code (octet) des objets comme les classes, les fonctions, les méthodes, les modules, etc. peut être incluse en définissant l'option code=True.

Et si vous avez besoin d'une autre vue sur les données en direct, Pympler

le module muppyest utilisé pour la surveillance en ligne d'une application Python et le module Class Trackerfournit une analyse hors ligne de la durée de vie des objets Python sélectionnés.

serv-inc
la source
cette fonction est assez lente pour les objets plus gros. Existe-t-il un équivalent "rapide" qui fonctionne pour les objets auto-créés?
Shuklaswag
Je ne l'ai pas encore testé, mais org.apache.spark.util.SizeEstimatorpeut être pertinent
Shuklaswag
1
@Shuklaswag: si vous utilisez une étincelle, cela pourrait bien l'être. Pensez-vous que l'estimation de conversion + Java est plus rapide que les méthodes intégrées de python? Ou ai-je mal compris?
serv-inc
3
Il peut être intéressant de noter qu'il pymplera la capacité de prendre en compte la taille de code exécutable des fonctions et autres objets appelables et objets de code.
mtraceur
J'obtiens une TypeErrorexception: "L'objet 'NoneType' n'est pas appelable" chaque fois que mon objet personnalisé a un sous-objet dans son "arborescence" avec une valeur None. Existe-t-il une solution rapide à cela?
James Hirschorn
81

Pour les tableaux numpy, getsizeofne fonctionne pas - pour moi, il renvoie 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
Mike Dewar
la source
29
> 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 est spécifique à l'implémentation. docs.python.org/library/sys.html#sys.getsizeof
warvariuc
33
"Si vous utilisez un tableau numpy ( docs.scipy.org/doc/numpy/reference/arrays.ndarray.html ), vous pouvez utiliser l'attribut 'ndarray.nbytes' pour évaluer sa taille en mémoire." stackoverflow.com/a/15591157/556413
glarrain
17
Je suppose que 40 octets est correct, mais getsizeof()ne vous donne que la taille de l'objet (l'en-tête du tableau), pas des données à l'intérieur. Idem pour les conteneurs python où sys.getsizeof([1,2,4]) == sys.getsizeof([1,123**456,4]) == 48, tandis quesys.getsizeof(123**456) = 436
yota
3
Il semble que la getsizeof()fonction ait été modifiée à un moment donné pour renvoyer la valeur attendue.
dshin
16

Python 3.8 (T1 2019) changera 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 problème 33597 et le travail d' Inada Naoki ( methane) autour de Compact PyGC_Head et PR 7043

Cette idée réduit la taille de PyGC_Head à deux mots .

Actuellement, PyGC_Head prend trois mots ; gc_prev,, gc_nextet gc_refcnt.

  • gc_refcnt est utilisé lors de la collecte, pour la suppression de l'essai.
  • gc_prev est utilisé pour le suivi et le non-suivi.

Donc, si nous pouvons éviter le suivi / non-suivi lors de la suppression de l'essai gc_prevet gc_refcntpartager le même espace mémoire.

Voir commit d5c875b :

Suppression d'un Py_ssize_tmembre de PyGC_Head.
La taille de tous les objets suivis par le GC (par exemple, tuple, list, dict) est réduite de 4 ou 8 octets.

VonC
la source
14

Cela peut être plus compliqué qu'il n'y paraît selon la façon dont vous voulez compter les choses. Par exemple, si vous avez une liste d'entiers, voulez-vous la taille de la liste contenant les références aux entiers? (c.-à-d. 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 empêcher le double comptage lorsque deux objets contiennent des références à le même objet.

Vous voudrez peut-être jeter un oeil à l'un des profileurs de mémoire python, comme pysizer pour voir s'ils répondent à vos besoins.

Brian
la source
10

Ayant moi-même rencontré ce problème plusieurs fois, j'ai rédigé une petite fonction (inspirée de la réponse de @ aaron-hall) et des tests qui font ce que j'aurais attendu de sys.getsizeof:

https://github.com/bosswissam/pysize

Si la trame de fond vous intéresse, la voici

EDIT: Joindre le code ci-dessous pour une référence facile. Pour voir le code le plus récent, veuillez vérifier 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
wissam
la source
7

Voici un script rapide que j'ai écrit sur la base des réponses précédentes pour répertorier la taille de toutes les variables

for i in dir():
    print (i, sys.getsizeof(eval(i)) )
alexey
la source
Ce n'est pas faux, c'est ambigu. sys.getsizeof renverra toujours la valeur nécessaire, il n'est donc pas nécessaire de perdre les performances avec try..except.
der_fenix
oh, c'est un bon point et je n'y ai pas pensé - le code sous sa forme actuelle montre simplement comment il a été écrit chronologiquement - d'abord je connaissais numpy (d'où nbytes), puis j'ai cherché une solution plus générique . Merci pour l'explication _ / \ _
alexey
7

Vous pouvez sérialiser l'objet pour dériver une mesure étroitement liée à la taille de l'objet:

import pickle

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

Si vous souhaitez mesurer des objets qui ne peuvent pas être décapés (par exemple à cause d'expressions lambda), cloudpickle peut être une solution.

Quelqu'un
la source
4

Utilisez sys.getsizeof () si vous NE souhaitez PAS inclure les tailles des objets liés (imbriqués).

Cependant, si vous voulez compter les sous-objets imbriqués dans des listes, des dict, des ensembles, des tuples - et généralement c'est ce que vous recherchez - utilisez la fonction récursive deep sizeof () comme indiqué ci-dessous:

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

Vous pouvez également trouver cette fonction dans la boîte à outils astucieuse , ainsi que de nombreuses autres lignes utiles:

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

Marcin Wojnarski
la source
3

Si vous n'avez pas besoin de la taille exacte de l'objet mais pour savoir approximativement sa taille, une façon rapide (et sale) consiste à laisser le programme s'exécuter, à dormir pendant une période prolongée et à vérifier l'utilisation de la mémoire (par ex. : Moniteur d'activité de Mac) par ce processus python particulier. Cela serait efficace lorsque vous essayez de trouver la taille d'un seul grand objet dans un processus python. Par exemple, j'ai récemment voulu vérifier l'utilisation de la mémoire d'une nouvelle structure de données et la comparer avec 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 j'ai vérifié la taille du processus, puis j'ai fait la même chose avec l'autre structure de données. J'ai découvert que le processus Python avec un ensemble prend deux fois plus de mémoire que la nouvelle structure de données. Encore une fois, vous ne voudriez pas t pouvoir 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 devient grande, cela devient proche car 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.

picmate 涅
la source
1
La question demande comment le faire en python , pas seulement trouver l'utilisation de la mémoire des objets python, et utiliser le moniteur d'activité d'un Mac ou tout autre logiciel similaire n'utilise pas par programme python. Cela étant dit, la vérification de l'utilisation de la mémoire des processus python de cette manière est généralement un bon moyen de s'assurer que rien ne s'est mal passé ...
Tom Wyllie
@ TomWyllie, Merci, mais le fait de voter pour cette réponse porte la connotation négative que la réponse elle-même est fausse et ne fait rien. La méthode que je mentionne n'est peut-être pas implémentée en Python, mais c'est un moyen pratique d'obtenir une estimation approximative de la taille d'un objet Python. Je savais que je ne répondais pas à la question exacte, cependant, la méthode pourrait être utile à quelqu'un d'autre pour obtenir un résultat similaire.
picmate
1

Vous pouvez utiliser getSizeof () comme mentionné ci-dessous pour déterminer la taille d'un objet

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")
forkdbloke
la source
0

J'utilise cette astuce ... Peut ne pas être précis sur les petits objets, mais je pense que c'est beaucoup plus précis pour un objet complexe (comme la surface pygame) plutôt que 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

Sur mes fenêtres 10, python 3.7.3, la sortie est:

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
Hzzkygcs
la source