Как определить размер объекта в Python?

В C мы можем найти размер int, charи т. д. Я хочу знать, как получить размер объектов, таких как строка, целое число и т. д. в Python.

Смежный вопрос: сколько байтов на элемент содержится в списке Python (кортеж)?

Я использую файл XML, который содержит поля размера, которые определяют размер значения. Я должен разобрать этот XML и сделать свое кодирование. Когда я хочу изменить значение определенного поля, я проверю поле размера этого значения. Здесь я хочу сравнить, имеет ли новое значение, которое я собираюсь ввести, тот же размер, что и в XML. Мне нужно проверить размер нового значения. В случае строки я могу сказать ее длину. Но в случае int, float и т. Д. Я запутался.

17 ответов

Просто используйте функцию sys.getsizeof, определенную в sys модуль.

sys.getsizeof(object[, default]):

Вернуть размер объекта в байтах. Объект может быть любым типом объекта. Все встроенные объекты будут возвращать правильные результаты, но это не должно выполняться для сторонних расширений, поскольку это зависит от реализации.

default Аргумент позволяет определить значение, которое будет возвращено, если тип объекта не предоставляет средства для получения размера и вызовет TypeError,

getsizeof вызывает объект __sizeof__ метод и добавляет дополнительные издержки сборщика мусора, если объект управляется сборщиком мусора.

Пример использования в 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

Если вы в Python < 2.6 и не имеете sys.getsizeof Вы можете использовать этот обширный модуль вместо. Никогда не использовал это все же.

Как определить размер объекта в Python?

Ответ "Просто используйте sys.getsizeof" не является полным ответом.

Этот ответ работает непосредственно для встроенных объектов, но он не учитывает, что эти объекты могут содержать, в частности, какие типы, такие как кортежи, списки, диктовки и наборы, содержат. Они могут содержать экземпляры друг друга, а также числа, строки и другие объекты.

Более полный ответ

Используя 64-битный Python 3.6 из дистрибутива Anaconda, с помощью sys.getsizeof, я определил минимальный размер следующих объектов и обратите внимание, что устанавливает и диктует предварительное выделение пространства, поэтому пустые не увеличиваются снова до тех пор, пока не истечет установленное количество (что может зависит от реализации языка):

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    doesn't 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.

Как вы это интерпретируете? Хорошо, скажем, у вас есть набор из 10 предметов. Если каждый элемент имеет размер 100 байт, то насколько велика вся структура данных? Сам набор равен 736, потому что его размер увеличился до 736 байт. Затем вы добавляете размер элементов, так что всего получается 1736 байт.

Некоторые предостережения для определения функций и классов:

Обратите внимание, что у каждого определения класса есть прокси __dict__ (48 байт) структура для атрибутов класса. Каждый слот имеет дескриптор (например, property) в определении класса.

Временные интервалы начинаются с 48 байтов на их первом элементе и увеличиваются на 8 каждый дополнительный. Только пустые объекты со слотами имеют 16 байтов, и экземпляр без данных не имеет большого смысла.

Кроме того, каждое определение функции имеет объекты кода, возможно, строки документации и другие возможные атрибуты, даже __dict__,

Анализ Python 2.7, подтвержденный guppy.hpy а также 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    doesn't 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.

Обратите внимание, что словари ( но не наборы) получили более компактное представление в Python 3.6

Я думаю, что 8 байтов на каждый элемент для справки имеют большой смысл на 64-битной машине. Эти 8 байтов указывают на место в памяти, в котором находится содержащийся элемент. 4 байта имеют фиксированную ширину для юникода в Python 2, если я правильно помню, но в Python 3 str становится юникодом ширины, равной максимальной ширине символов.

(И больше о слотах, смотрите этот ответ)

Рекурсивный посетитель для более полной функции

Чтобы охватить большинство этих типов, я написал эту рекурсивную функцию, чтобы попытаться оценить размер большинства объектов Python, включая большинство встроенных функций, типы в модуле коллекций и пользовательские типы (в слотах и ​​в других случаях):

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)

И я протестировал это довольно случайно (я должен протестировать это):

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

Это как бы разбивает определения классов и определений функций, потому что я не берусь за все их атрибуты, но, поскольку они должны существовать в процессе только один раз в памяти, их размер на самом деле не имеет большого значения.

Пакет Pympler 's asizeof Модуль может сделать это.

Используйте следующим образом:

from pympler import asizeof
asizeof.asizeof(my_object)

В отличие от sys.getsizeof, это работает для ваших самостоятельно созданных объектов. Это даже работает с 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

Как уже упоминалось,

(Байт) размер кода объектов, таких как классы, функции, методы, модули и т. Д., Может быть включен с помощью параметра настройки code=True ,

И если вам нужен другой взгляд на живые данные, Pympler's

модуль muppy используется для онлайн-мониторинга приложения и модуля Python Class Tracker обеспечивает автономный анализ времени жизни выбранных объектов Python.

Для массивов NumPy, getsizeof не работает - для меня это всегда возвращает 40 по некоторым причинам:

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

Тогда (в ipython):

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

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

К счастью, хотя:

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

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

Вы можете сериализовать объект, чтобы получить меру, которая тесно связана с размером объекта:

import pickle

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

Если вы хотите измерить объекты, которые нельзя мариновать (например, из-за лямбда-выражений), решением могут быть укроп или тучи.

Используйте sys.getsizeof(), если вы НЕ хотите включать размеры связанных (вложенных) объектов.

Однако, если вы хотите подсчитать подобъекты, вложенные в списки, словари, наборы, кортежи - а обычно это именно то, что вы ищете - используйте рекурсивную функцию deep sizeof(), как показано ниже:

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

Вы также можете найти эту функцию в изящном наборе инструментов вместе со многими другими полезными однострочниками:

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

Python 3.8 (Q1 2019) изменит некоторые результаты sys.getsizeofРэймонд Хеттингер объявил здесь:

Контейнеры Python на 8 байтов меньше на 64-битных сборках.

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

Это после номера 33597 и Инада Наоки ( methane ) работает вокруг Compact PyGC_Head и PR 7043

Эта идея уменьшает размер PyGC_Head до двух слов.

В настоящее время PyGC_Head занимает три слова; gc_prev, gc_next, а также gc_refcnt,

  • gc_refcnt используется при сборе, для пробного удаления.
  • gc_prev используется для отслеживания и отслеживания.

Так что, если мы сможем избежать отслеживания / отслеживания во время пробного удаления, gc_prev а также gc_refcnt может использовать то же пространство памяти

Смотрите коммит d5c875b:

Удален один Py_ssize_t член от PyGC_Head,
Размер всех отслеживаемых объектов GC (например, tuple, list, dict) уменьшается на 4 или 8 байт.

Это может быть сложнее, чем кажется, в зависимости от того, как вы хотите считать вещи. Например, если у вас есть список целых, хотите ли вы размер списка, содержащего ссылки на эти целые? (т. е. только список, а не то, что содержится в нем), или вы хотите включить фактические данные, на которые указывает, в этом случае вам нужно иметь дело с дублирующимися ссылками, и как предотвратить двойной счет, когда два объекта содержат ссылки на тот же объект.

Возможно, вы захотите взглянуть на один из профилировщиков памяти python, например pysizer, чтобы увидеть, соответствуют ли они вашим потребностям.

Сам много раз сталкиваясь с этой проблемой, я написал небольшую функцию (вдохновленную ответом @aaron-hall) и тесты, которые выполняют то, что я ожидал от sys.getsizeof:

https://github.com/bosswissam/pysize

Если вы заинтересованы в предыстории, вот она

РЕДАКТИРОВАТЬ: Прикрепление кода ниже для удобства. Чтобы увидеть самый последний код, пожалуйста, проверьте ссылку на 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

Вот быстрый скрипт, который я написал на основе предыдущих ответов на размеры списка всех переменных

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

Используйте следующую функцию, чтобы получить фактический размер объекта python:

      import sys
import gc

def actualsize(input_obj):
    memory_size = 0
    ids = set()
    objects = [input_obj]
    while objects:
        new = []
        for obj in objects:
            if id(obj) not in ids:
                ids.add(id(obj))
                memory_size += sys.getsizeof(obj)
                new.append(obj)
        objects = gc.get_referents(*new)
    return memory_size

actualsize([1, 2, [3, 4, 5, 1]])

Ссылка: https://towardsdatascience.com/the-strange-size-of-python-objects-in-memory-ce87bdfbb97f

Если вам не нужен точный размер объекта, но нужно примерно знать, насколько он велик, один быстрый (и грязный) способ - запустить программу, не спать в течение продолжительного периода времени и проверить использование памяти (например,: Монитор активности Mac) этим конкретным процессом python. Это будет эффективно, когда вы пытаетесь найти размер одного большого объекта в процессе Python. Например, недавно я хотел проверить использование памяти новой структурой данных и сравнить ее с установленной структурой данных Python. Сначала я записал элементы (слова из большой общедоступной книги) в набор, затем проверил размер процесса, а затем проделал то же самое с другой структурой данных. Я обнаружил, что процесс Python с множеством занимает вдвое больше памяти, чем новая структура данных. Опять же, вы не сможете точно сказать, что память, используемая процессом, равна размеру объекта. По мере того как размер объекта становится большим, он становится близким, так как объем памяти, используемой остальной частью процесса, становится незначительным по сравнению с размером объекта, который вы пытаетесь отслеживать.

Если производительность не является проблемой, самое простое решение — собрать и измерить:

      import pickle

data = ...
len(pickle.dumps(data))

Я использую этот трюк... Может быть неточным для небольших объектов, но я думаю, что он намного точнее для сложного объекта (например, поверхности pygame), чем 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

В моих окнах 10, python 3.7.3 вывод:

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

Возможно, это не самый подходящий ответ, но меня интересовало только хранение и поиск объектов. Таким образом, было достаточно сбросить объект как рассол и проверить размер рассола.

Вы можете использовать getSizeof(), как указано ниже, для определения размера объекта.

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

Первый: ответ.

import sys

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

Обсуждение:
В Python вы никогда не сможете получить доступ к "прямым" адресам памяти. Зачем тогда вам нужно знать или знать, сколько таких адресов занято данным объектом? Это вопрос, который совершенно неуместен на этом уровне абстракции. Когда вы рисуете свой дом, вы не спрашиваете, какие частоты света поглощаются или отражаются каждым из составляющих атомов краски, вы просто спрашиваете, какой это цвет - детали физических характеристик, которые создают этот цвет. находятся в стороне. Точно так же количество байтов памяти, которое занимает данный объект Python, не относится к делу.

Итак, почему вы пытаетесь использовать Python для написания кода на C?:)

Другие вопросы по тегам