uma - orientação a objetos de python




Como faço para determinar o tamanho de um objeto no Python? (6)

Em C, podemos encontrar o tamanho de um int , char , etc. Eu quero saber como obter o tamanho de objetos como uma string, inteiro, etc. no Python.

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

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


Como faço para determinar o tamanho de um objeto no Python?

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

Essa resposta funciona diretamente para objetos internos, mas não leva em conta o que esses objetos podem conter, especificamente, quais tipos, como tuplas, listas, canais e conjuntos contêm. Eles podem conter instâncias uns aos outros, bem como 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 observei que os conjuntos e ditos pré-alocam espaço para que os vazios não cresçam novamente até depois de um valor definido (o que pode variar de acordo com a implementação da linguagem):

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 tiver 100 bytes cada, qual a dimensão da estrutura de dados? O conjunto é o próprio 736 porque dimensionou uma vez para 736 bytes. Então você adiciona o tamanho dos itens, então são 1736 bytes no total

Algumas advertências para definições de funções e classes:

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

Instâncias com slots começam com 48 bytes em seu primeiro elemento e aumentam em 8 cada um adicional. Somente objetos com slot vazios 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é mesmo um __dict__ .

Análise do Python 2.7, confirmada com guppy.hpy e 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 lugar na memória em que o item contido está. Os 4 bytes são de 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 mais informações sobre slots, veja esta resposta )

Visitante recursivo para uma função mais completa

Para cobrir a maioria desses tipos, eu escrevi essa função recursiva para tentar estimar o tamanho da maioria dos objetos Python, incluindo a maioria dos builtins, tipos no módulo de coleções e tipos personalizados (com ou sem slot):

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 isso casualmente (eu deveria testá-lo):

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

É uma espécie de quebra de definições de classe e definições de função, porque eu não vou atrás de todos os seus atributos, mas desde que eles só devem existir uma vez na memória para o processo, seu tamanho realmente não importa muito.


Apenas use a função sys.getsizeof definida no módulo sys .

sys.getsizeof(object[, default]) :

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

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

getsizeof chama o método __sizeof__ do objeto e adiciona uma sobrecarga adicional ao coletor de lixo se o objeto for gerenciado pelo coletor de lixo.

Exemplo de uso, em 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ê está em python <2.6 e não tem sys.getsizeof você pode usar este extenso módulo . Nunca usei isso.


Isso pode ser mais complicado do que parece, dependendo de como você quer contar as coisas. Por exemplo, se você tem uma lista de ints, você quer o tamanho da lista contendo as referências aos ints? (ou seja, lista apenas, não o que está contido nela), ou você deseja incluir os dados reais apontados, caso em que 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 querer dar uma olhada em um dos profilers de memória python, como o pysizer para ver se eles atendem às suas necessidades.


O Python 3.8 (Q1 2019) irá alterar alguns dos resultados do sys.getsizeof , conforme anunciado 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 o problema 33597 e o trabalho de Inada Naoki ( methane ) em torno do Compact PyGC_Head e do PR 7043

Essa ideia reduz o tamanho de PyGC_Head para duas palavras .

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

  • gc_refcnt é usado ao coletar, para exclusão de teste.
  • gc_prev é usado para rastreamento e desmarcação.

Portanto, se pudermos evitar o rastreamento / desmarcamento durante a exclusão da avaliação, gc_prev e gc_refcnt poderão compartilhar o mesmo espaço de memória.

Veja commit d5c875b :

Removido um membro PyGC_Head do PyGC_Head .
Todos os objetos rastreados pelo GC (por exemplo, tupla, lista, ordem) são reduzidos em 4 ou 8 bytes.


Para matrizes numpy, o getsizeof não funciona - para mim, ele 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

Primeiro: uma resposta.

import sys

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

Discussão:
No Python, você nunca pode acessar endereços de memória "diretos". Por que, então, você precisaria ou desejaria saber quantos endereços são ocupados por um determinado objeto? É uma questão totalmente inadequada nesse nível de abstração. Quando você está pintando sua casa, você não pergunta que freqüências de luz são absorvidas ou refletidas por cada um dos átomos constituintes dentro da tinta, você apenas pergunta qual é a cor - os detalhes das características físicas que criam essa cor. estão fora do ponto. Da mesma forma, o número de bytes de memória que um determinado objeto Python ocupa está além do ponto.

Então, por que você está tentando usar o Python para escrever código C? :)







sizeof