Como determino o tamanho de um objeto em Python?


682

Eu quero saber como obter o tamanho de objetos como uma seqüência de caracteres, número inteiro, etc. em Python.

Pergunta relacionada: Quantos bytes por elemento existem em uma lista Python (tupla)?

Estou usando um arquivo XML que contém campos de tamanho que especificam o tamanho do valor. Eu devo analisar esse XML e fazer minha codificação. Quando quiser alterar o valor de um campo específico, verificarei o campo de tamanho desse valor. Aqui, quero comparar se o novo valor que devo inserir é do mesmo tamanho que no XML. Preciso verificar o tamanho do novo valor. No caso de uma string, posso dizer que é o comprimento. Mas no caso de int, float etc. estou confuso.

Respostas:


665

Basta usar a função sys.getsizeof definida no sysmódulo.

sys.getsizeof(object[, default]):

Retorne o tamanho de um objeto em bytes. O objeto pode ser qualquer tipo de objeto. Todos os objetos internos retornam resultados corretos, mas isso não precisa ser verdadeiro para extensões de terceiros, pois é específico da implementação.

O defaultargumento permite definir um valor que será retornado se o tipo de objeto não fornecer meios para recuperar o tamanho e causar a TypeError.

getsizeofchama o __sizeof__método do objeto e adiciona uma sobrecarga adicional do coletor de lixo se o objeto for gerenciado pelo coletor de lixo.

Exemplo de uso, no 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 você estiver em python <2.6 e não o tiver, sys.getsizeofpoderá usar este extenso módulo . Nunca usei embora.


181
Por favor, adicione ao aviso de que não será uma realidade para objetos aninhados ou dicts aninhadas ou dicts em listas etc.
JohnnyM

8
@ ChaimG é porque todo objeto usa apenas 32 bytes !! O resto são referências a outros objetos. Se você deseja contabilizar os objetos referenciados, precisa definir o __sizeof__método para sua classe. A dictclasse python interna o define, é por isso que você obtém o resultado correto ao usar o objeto do tipo dict.
Nosklo 11/04

19
O aviso e as exceções a este trabalho abrangem quase todos os casos de uso, tornando a getsizeoffunção de pouco valor pronta para uso.
Robino

7
por que o número inteiro 2 é armazenado em 24 bytes?
Saher Ahwal 19/03/18

4
@SaherAhwal não é apenas um inteiro, mas um objeto completo com métodos, atributos, endereços ...
nosklo

369

Como determino o tamanho de um objeto em Python?

A resposta "Apenas use sys.getsizeof" não é uma resposta completa.

Essa resposta faz trabalho para builtin objetos diretamente, mas não leva em conta o que esses objetos podem conter, especificamente, quais os tipos, tais como objetos personalizados, tuplas, listas, dicts e conjuntos contêm. Eles podem conter instâncias um do outro, além de números, seqüências de caracteres e outros objetos.

Uma resposta mais completa

Usando o Python 3.6 de 64 bits da distribuição Anaconda, com sys.getsizeof, determinei o tamanho mínimo dos seguintes objetos e observe que os conjuntos e os dicionários pré-alocam espaço para que os vazios não cresçam novamente até depois de um determinado valor (o que pode variar de acordo com a implementação do idioma):

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 você interpreta isso? Bem, digamos que você tenha um conjunto com 10 itens. Se cada item tem 100 bytes cada, qual é o tamanho de toda a estrutura de dados? O conjunto é o próprio 736 porque foi dimensionado uma vez para 736 bytes. Então você adiciona o tamanho dos itens, totalizando 1736 bytes

Algumas advertências para definições de função e classe:

Observe que cada definição de classe possui uma estrutura de proxy __dict__(48 bytes) para atributos de classe. Cada slot possui um descritor (como a property) na definição de classe.

Instâncias com slot começam com 48 bytes no primeiro elemento e aumentam em 8 a cada adicional. Somente objetos com espaço vazio têm 16 bytes, e uma instância sem dados faz muito pouco sentido.

Além disso, cada definição de função possui objetos de código, talvez docstrings e outros atributos possíveis, até a __dict__.

Observe também que usamos sys.getsizeof()porque nos preocupamos com o uso do espaço marginal, que inclui a sobrecarga da coleta de lixo para o objeto, nos documentos :

getsizeof () chama o __sizeof__método do objeto e adiciona uma sobrecarga adicional do coletor de lixo se o objeto for gerenciado pelo coletor de lixo.

Observe também que o redimensionamento de listas (por exemplo, anexando-as repetidamente) faz com que pré-alocem espaço, da mesma forma que sets e dict. A partir do código-fonte 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);

Data histórica

Análise do Python 2.7, confirmada com guppy.hpye 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.

Observe que os dicionários ( mas não os conjuntos ) obtiveram uma representação mais compacta no Python 3.6

Eu acho que 8 bytes por item adicional para referência faz muito sentido em uma máquina de 64 bits. Esses 8 bytes apontam para o local na memória em que o item contido está. Os 4 bytes têm largura fixa para unicode no Python 2, se bem me lembro, mas no Python 3, str se torna um unicode de largura igual à largura máxima dos caracteres.

(E para saber mais sobre slots, veja esta resposta )

Uma função mais completa

Queremos uma função que pesquise os elementos em listas, tuplas, conjuntos, ditados, obj.__dict__'s e obj.__slots__, além de outras coisas que talvez ainda não tenhamos pensado.

Queremos contar gc.get_referentscom essa pesquisa porque ela funciona no nível C (tornando-a muito rápida). A desvantagem é que get_referents pode retornar membros redundantes, portanto, precisamos garantir que não contemos duas vezes.

Classes, módulos e funções são singletons - eles existem uma vez na memória. Não estamos tão interessados ​​em seu tamanho, pois não há muito o que fazer sobre eles - eles fazem parte do programa. Portanto, evitaremos contá-los se eles forem referenciados.

Usaremos uma lista negra de tipos para não incluir o programa inteiro em nossa contagem de tamanhos.

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 isso com a seguinte função na lista de permissões, a maioria dos objetos sabe como se mover para fins de coleta de lixo (que é aproximadamente o que estamos procurando quando queremos saber o quão caro são na memória certos objetos. Essa funcionalidade é usada por gc.get_referents.) No entanto, essa medida terá um alcance muito mais amplo do que pretendíamos se não tomarmos cuidado.

Por exemplo, as funções sabem bastante sobre os módulos em que são criadas.

Outro ponto de contraste é que as seqüências de caracteres que são chaves nos dicionários geralmente são internadas para que não sejam duplicadas. A verificação id(key)também permitirá evitar a contagem de duplicatas, o que faremos na próxima seção. A solução da lista negra ignora a contagem de chaves que são seqüências de caracteres por completo.

Tipos de lista de permissões, visitante recursivo (implementação antiga)

Para cobrir a maioria desses tipos, em vez de depender do módulo gc, escrevi essa função recursiva para tentar estimar o tamanho da maioria dos objetos Python, incluindo a maioria dos componentes internos, tipos no módulo de coleções e tipos personalizados (com ou sem fenda) .

Esse tipo de função fornece muito mais controle refinado sobre os tipos que contaremos para uso de memória, mas tem o risco de deixar os tipos de fora:

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 eu testei casualmente (eu deveria 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

Essa implementação divide-se nas definições de classe e nas definições de função porque não seguimos todos os seus atributos, mas como eles só devem existir uma vez na memória para o processo, seu tamanho realmente não importa muito.


5
Você poderia acrescentar que esta resposta é específica para CPython (que está implícito por você ficando Python através Anaconda)
gerrit

1
O CPython é a implementação de referência, e acabei de revisar os documentos on-line do jython que fornecem a mesma API, por isso acredito que isso funcionará em outras implementações, desde que implementem as APIs.
Aaron Hall

para mim não funcionou para mascarado e matrizes numpy desmascarados stackoverflow.com/q/58675479/2132157
GM

95

O módulo do pacote Pymplerasizeof pode fazer isso.

Use da seguinte maneira:

from pympler import asizeof
asizeof.asizeof(my_object)

Ao contrário sys.getsizeof, ele funciona para seus objetos criados automaticamente . Até funciona com 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 mencionado ,

O tamanho do código (byte) de objetos como classes, funções, métodos, módulos etc. pode ser incluído pela opção de configuração code=True.

E se você precisar de outra visão sobre dados ao vivo, o Pympler

O módulo muppyé usado para o monitoramento on-line de um aplicativo Python e o módulo Class Trackerfornece análise off-line da vida útil dos objetos Python selecionados.


essa função é bastante lenta para objetos maiores. Existe um equivalente "rápido" que funciona para objetos criados automaticamente?
21717 Shuklaswag

Eu não testei ainda, mas org.apache.spark.util.SizeEstimatorpode ser relevante
Shuklaswag

1
@Shuklaswag: se você usa faísca, pode ser que seja. Você acha que a estimativa de conversão + Java é mais rápida que os métodos internos do python? Ou eu entendi errado?
serv-inc

3
Vale a pena notar que pymplerpossui recursos para levar em consideração o tamanho do código executável de funções e outros códigos de chamada e objetos de código.
Mtraceur

Eu recebo uma TypeErrorexceção: "O objeto 'NoneType' não pode ser chamado" sempre que meu objeto personalizado tiver algum subobjeto em sua "árvore" com valor None. Existe alguma solução rápida para isso?
James Hirschorn 8/18

81

Para matrizes numpy, getsizeofnão funciona - para mim, sempre retorna 40 por algum motivo:

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

Então (no ipython):

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

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

Felizmente, porém:

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

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

29
> Todos os objetos internos retornam resultados corretos, mas isso não precisa ser verdadeiro para extensões de terceiros, pois é específico da implementação. docs.python.org/library/sys.html#sys.getsizeof
warvariuc

33
"Se você estiver usando uma matriz numpy ( docs.scipy.org/doc/numpy/reference/arrays.ndarray.html ), poderá usar o atributo 'ndarray.nbytes' para avaliar seu tamanho na memória." stackoverflow.com/a/15591157/556413
glarrain

17
Eu acho que 40 bytes está correto, no entanto, getsizeof()apenas fornece o tamanho do objeto (o cabeçalho da matriz), não dos dados internos. Mesmo para recipientes python, onde sys.getsizeof([1,2,4]) == sys.getsizeof([1,123**456,4]) == 48, ao mesmo temposys.getsizeof(123**456) = 436
Yota

3
Parece que a getsizeof()função foi alterada em algum momento para retornar o valor esperado.
dshin

14

Isso pode ser mais complicado do que parece, dependendo de como você deseja contar as coisas. Por exemplo, se você possui uma lista de entradas, deseja o tamanho da lista que contém as referências às entradas? (ou seja, apenas lista, não o que está contido nela) ou você deseja incluir os dados reais apontados; nesse caso, você precisa lidar com referências duplicadas e como evitar a contagem dupla quando dois objetos contêm referências a o mesmo objeto.

Você pode dar uma olhada em um dos criadores de perfil de memória python, como o pysizer, para ver se eles atendem às suas necessidades.


14

O Python 3.8 (primeiro trimestre de 2019) mudará alguns dos resultados de sys.getsizeof, conforme anunciado aqui por Raymond Hettinger:

Contêineres Python são 8 bytes menores em compilações de 64 bits.

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

Isso ocorre após a edição 33597 e o trabalho de Inada Naoki ( methane) em torno do Compact PyGC_Head e PR 7043

Essa ideia reduz o tamanho do PyGC_Head para duas palavras .

Atualmente, PyGC_Head leva três palavras ; gc_prev,, gc_nexte gc_refcnt.

  • gc_refcnt é usado na coleta, para exclusão do teste.
  • gc_prev é usado para rastrear e não rastrear.

Portanto, se pudermos evitar o rastreamento / rastreamento durante a exclusão do teste gc_preve gc_refcntcompartilhar o mesmo espaço de memória.

Consulte commit d5c875b :

Removido um Py_ssize_tmembro de PyGC_Head.
Todo o tamanho dos objetos rastreados pelo GC (por exemplo, tupla, lista, ditado) é reduzido em 4 ou 8 bytes.


10

Tendo encontrado esse problema muitas vezes, escrevi uma pequena função (inspirada na resposta de @ aaron-hall) e testes que fazem o que eu esperava que sys.getsizeof fizesse:

https://github.com/bosswissam/pysize

Se você está interessado na história de fundo, aqui está

EDIT: Anexando o código abaixo para fácil referência. Para ver o código mais atualizado, consulte o link do 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

7

Aqui está um script rápido que escrevi com base nas respostas anteriores para listar tamanhos de todas as variáveis

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

Não está errado, é ambíguo. sys.getsizeof sempre retornará valor, então não há necessidade de perder desempenho com try..except.
der_fenix

ah, esse é um bom argumento e eu não pensei nisso - o código na forma como está agora mostra apenas como foi cronologicamente escrito - primeiro eu sabia sobre numpy (daí nbytes), depois procurei uma solução mais genérica . Obrigado pela explicação _ / \ _
alexey

7

Você pode serializar o objeto para derivar uma medida que esteja intimamente relacionada ao tamanho do objeto:

import pickle

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

Se você deseja medir objetos que não podem ser decapados (por exemplo, devido a expressões lambda), o cloudpickle pode ser uma solução.


4

Use sys.getsizeof () se você NÃO deseja incluir tamanhos de objetos vinculados (aninhados).

No entanto, se você quiser contar subobjetos aninhados em listas, dictos, conjuntos, tuplas - e geralmente ISSO é o que você está procurando - use a função sizeof () profunda recursiva, como mostrado abaixo:

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

Você também pode encontrar esta função na caixa de ferramentas bacana , junto com muitas outras opções úteis:

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


3

Se você não precisar do tamanho exato do objeto, mas aproximadamente para saber o tamanho dele, uma maneira rápida (e suja) é deixar o programa funcionar, dormir por um longo período de tempo e verificar o uso da memória (ex. : Monitor de atividade do Mac) por esse processo python específico. Isso seria eficaz quando você estiver tentando encontrar o tamanho de um único objeto grande em um processo python. Por exemplo, recentemente eu queria verificar o uso da memória de uma nova estrutura de dados e compará-la com a da estrutura de dados do Python. Primeiro, escrevi os elementos (palavras de um grande livro de domínio público) em um conjunto, depois verifiquei o tamanho do processo e depois fiz a mesma coisa com a outra estrutura de dados. Descobri que o processo Python com um conjunto está consumindo o dobro de memória que a nova estrutura de dados. Mais uma vez, você não ser capaz de dizer exatamente que a memória usada pelo processo é igual ao tamanho do objeto. À medida que o tamanho do objeto aumenta, isso fica próximo à medida que a memória consumida pelo restante do processo se torna insignificante em comparação ao tamanho do objeto que você está tentando monitorar.


1
A pergunta pergunta como fazê-lo em python , não apenas encontrando o uso da memória de objetos python, e o uso do monitor de atividades do Mac ou qualquer outro software similar não está usando programaticamente o python. Dito isto, verificando o uso de memória de processos python, desta forma, geralmente é uma boa maneira de ter certeza que nada tem de errado foi ...
Tom Wyllie

@ TomWyllie, obrigado, mas o voto negativo dessa resposta carrega a conotação negativa de que a resposta em si está errada e não realiza nada. O método mencionado não pode ser implementado no Python, mas é uma maneira útil de obter uma estimativa aproximada do tamanho de um objeto Python. Eu sabia que não estava respondendo à pergunta exata; no entanto, o método poderia ser útil para outra pessoa obter um resultado semelhante.
você precisa saber é o seguinte

1

Você pode usar getSizeof () conforme mencionado abaixo para determinar o tamanho de um objeto

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

0

Uso esse truque ... Pode não ser preciso em objetos pequenos, mas acho que é muito mais preciso para um objeto complexo (como a superfície do jogo), em vez de 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

No meu windows 10, python 3.7.3, a saída é:

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
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.