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

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

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

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

Ответ 1

Просто используйте функцию 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, вы можете использовать этот расширенный модуль. Никогда не использовал его.

Ответ 2

Как определить размер объекта в 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    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.

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

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

Обратите внимание, что каждое определение класса имеет прокси-структуру __dict__ (48 байт) для __dict__ класса. Каждый слот имеет дескриптор (как 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    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.

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

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

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

Более полная функция

Нам нужна функция, которая ищет элементы в списках, кортежах, наборах, dicts, obj.__dict__ 's и obj.__slots__, а также другие вещи, о которых мы, возможно, еще не подумали.

Мы хотим положиться на gc.get_referents чтобы выполнить этот поиск, потому что он работает на уровне C (что делает его очень быстрым). Недостатком является то, что get_referents может возвращать избыточные члены, поэтому мы должны убедиться, что мы не удваиваем счет.

Классы, модули и функции являются синглетонами - они существуют один раз в памяти. Нас не так интересует их размер, так как мы мало что можем с ними поделать - они являются частью программы. Поэтому мы не будем считать их, если на них будут ссылаться.

Мы собираемся использовать черный список типов, поэтому мы не включаем всю программу в наш счетчик размеров.

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

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

Например, функции знают достаточно много о модулях, в которых они созданы.

Еще одно отличие состоит в том, что строки, являющиеся ключами в словарях, обычно интернированы, поэтому они не дублируются. Проверка id(key) также позволит нам избежать подсчета дубликатов, что мы и сделаем в следующем разделе. Решение черного списка пропускает подсчет ключей, которые являются строками в целом.

Типы в белых списках, Рекурсивный посетитель (старая реализация)

Чтобы охватить большинство этих типов самостоятельно, вместо того, чтобы полагаться на модуль gc, я написал эту рекурсивную функцию, чтобы попытаться оценить размер большинства объектов 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

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

Ответ 3

Для массивов 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

Ответ 4

Модуль asizeof пакета 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

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

Ответ 5

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

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

Ответ 6

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

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

Ответ 7

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

https://github.com/bosswissam/pysize

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

EDIT: Прикрепление кода ниже для удобства. Чтобы просмотреть самый современный код, проверьте ссылку 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

Ответ 8

Если кто-то сталкивается с этим вопросом и нуждается в более "пуленепробиваемом" решении, чем в sys.getsizeof или в процедуре, предоставляемой Aaron Hall, есть рецепт здесь, который пытается решить проблемы, такие как классы и байт-коды, принципиально и гибко (слишком сложно реплицировать или осмысленно суммировать здесь, к сожалению).

Ответ 9

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

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

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

Это происходит после того, как проблема 33597 и Inada Naoki (methane) работают вокруг Compact PyGC_Head и PR 7043

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

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

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

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

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

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

Ответ 10

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

  import resource
  usage = resource.getrusage(resource.RUSAGE_SELF)
  maxrss_before = getattr(usage, 'ru_maxrss')
  your_object = func_to_create_your_object()
  maxrss_after = getattr(usage, 'ru_maxrss')
  print(maxrss_after - maxrss_before)

Ответ 11

Ни один из ответов здесь не является действительно общим. Следующее решение будет рекурсивно работать с любым типом объектов:

import gc
def get_obj_size(obj):
    marked = {id(obj)}
    obj_q = [obj]
    sz = 0
    while obj_q:
        cur_obj = obj_q.pop(0)
        sz += sys.getsizeof(cur_obj)
        all_refr = ((id(o), o) for o in  gc.get_referents(cur_obj))
        new_refr = list(filter(lambda o: o[0] not in marked, all_refr))
        if len(new_refr) > 0:
            refr_id, refr = zip(*new_refr)
            obj_q.extend(refr)
            marked.update(refr_id)
    return sz

Ответ 12

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

Ответ 13

Во-первых: ответ.

import sys

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

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

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