Как определить размер объекта в Python? - PullRequest
537 голосов
/ 16 января 2009

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

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

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

Ответы [ 10 ]

536 голосов
/ 16 января 2009

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

259 голосов
/ 19 мая 2015

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

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

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

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

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

(И больше о слотах, см. Этот ответ )

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

Нам нужна функция, которая ищет элементы в списках, кортежах, наборах, диктантах, obj.__dict__ и 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

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

77 голосов
/ 30 июля 2010

Для числовых массивов 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
61 голосов
/ 10 ноября 2015

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

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

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.

12 голосов
/ 16 января 2009

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

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

8 голосов
/ 22 июля 2016

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

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

for i in dir():
    print (i, sys.getsizeof(eval(i)) )
4 голосов
/ 19 февраля 2019

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

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

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 могут совместно использовать одно и то же пространство памяти.

См. commit d5c875b :

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

0 голосов
/ 10 апреля 2019

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

0 голосов
/ 16 января 2009

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

import sys

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

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

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

...