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


685

Я хочу знать, как получить размер объектов, таких как строка, целое число и т. Д. В Python.

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

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

Ответы:


665

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


182
Пожалуйста, добавьте к отказу от ответственности, что это не будет справедливо для вложенных объектов или вложенных диктовок или диктов в списках и т. Д.
JohnnyM

8
@ChaimG потому что каждый объект использует только 32 байта !! Остальные ссылки на другие объекты. Если вы хотите учесть ссылочные объекты, вы должны определить __sizeof__метод для вашего класса. Встроенный dictкласс python определяет его, поэтому вы получаете правильный результат при использовании объекта типа dict.
Носкло

19
Отказ от ответственности и исключения из этого рабочего документа охватывают почти все случаи использования, делающие getsizeofфункцию малозначимой из коробки.
Робиньо

7
почему целое число 2 хранится в 24 байтах?
Сахер Ахвал

4
@SaherAhwal это не просто целое число, а полный объект с методами, атрибутами, адресами ...
nosklo

372

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

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

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

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

Используя 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 байт) для атрибутов класса. У каждого слота есть дескриптор (например, a property) в определении класса.

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

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

Также обратите внимание, что мы используем это, sys.getsizeof()потому что мы заботимся об использовании предельного пространства, которое включает в себя накладные расходы на сборку мусора для объекта, из документов :

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

Также обратите внимание, что изменение размеров списков (например, повторное добавление к ним) заставляет их предварительно распределять пространство, аналогично наборам и диктам. Из исходного кода 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);

Исторические данные

Анализ 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

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


5
Вы можете добавить, что этот ответ относится только к CPython (что подразумевается при получении Python через Anaconda)
gerrit

1
CPython является эталонной реализацией, и я только что просмотрел онлайн-документы jython, которые предоставляют тот же API, поэтому я уверен, что это будет работать и в других реализациях, если они реализуют API.
Аарон Холл

у меня не сработало для масок и немаскированных массивов numpy stackoverflow.com/q/58675479/2132157
GM

96

В Pympler ПАКЕТ в 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.


эта функция довольно медленная для больших объектов. Существует ли «быстрый» эквивалент, который работает для самостоятельно созданных объектов?
Shuklaswag

Я еще не проверял это, но org.apache.spark.util.SizeEstimatorможет быть уместным
Shuklaswag

1
@Shuklaswag: если вы используете искру, это вполне может быть. Как вы думаете, конверсия + оценка Java быстрее, чем встроенные методы python? Или я неправильно понял?
серв-

3
Стоит отметить, что он pymplerимеет возможность принимать во внимание размер исполняемого кода функций и других вызываемых объектов и объектов кода.
mtraceur

Я получаю TypeErrorисключение: «Объект« NoneType »не может быть вызван» всякий раз, когда мой пользовательский объект имеет некоторый подобъект в своем «дереве» со значением None. Есть ли быстрый способ обойти это?
Джеймс Хиршорн

81

Для 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

29
> Все встроенные объекты будут возвращать правильные результаты, но это не должно выполняться для сторонних расширений, поскольку это зависит от реализации. docs.python.org/library/sys.html#sys.getsizeof
warvariuc

33
«Если вы используете пустой массив ( docs.scipy.org/doc/numpy/reference/arrays.ndarray.html ), то вы можете использовать атрибут ndarray.nbytes для оценки его размера в памяти». stackoverflow.com/a/15591157/556413
glarrain

17
Я бы предположил, что 40 байтов - это правильно, но getsizeof()дает только размер объекта (заголовок массива), а не данные внутри. То же самое для контейнеров Python, где sys.getsizeof([1,2,4]) == sys.getsizeof([1,123**456,4]) == 48, в то время какsys.getsizeof(123**456) = 436
Yota

3
Похоже, что getsizeof()функция была изменена в какой-то момент, чтобы вернуть ожидаемое значение.
дшин

16

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 байт.


14

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

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


10

Сам много раз сталкиваясь с этой проблемой, я написал небольшую функцию (вдохновленную ответом @ 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

7

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

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

Это не неправильно, это неоднозначно. sys.getsizeof всегда будет возвращать необходимое значение, поэтому нет необходимости терять производительность с помощью try..except.
der_fenix

о, это хороший момент, и я не думал об этом - код в той форме, в которой он сейчас находится, просто показывает, как он был написан в хронологическом порядке - сначала я знал о numpy (отсюда и nbytes), затем я искал более общее решение , Спасибо за объяснение _ / \ _
Алексей

7

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

import pickle

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

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


4

Используйте 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


3

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


1
Вопрос спрашивает , как сделать это в Python , а не просто найти использование памяти из объектов питона, а с помощью монитора активности УДСА или любого другого подобного программного обеспечения не программно с помощью питона. Тем не менее, проверка использования памяти процессами Python таким способом, как правило, является хорошим способом убедиться, что ничего не пошло не так ...
Том Уилли

@ TomWyllie, спасибо, но отрицание этого ответа несет негативную коннотацию, что сам ответ неверен и ничего не добивается. Метод, о котором я упоминаю, может быть не реализован в Python, но это удобный способ получить приблизительную оценку размера объекта Python. Я знал, что не отвечаю на точный вопрос, однако этот метод может быть полезен для кого-то еще, чтобы получить аналогичный результат.
picmate 涅

1

Вы можете использовать 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")

0

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

На моем Windows 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
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.