如何确定Python中对象的大小?

How do I determine the size of an object in Python?

在C中,我们可以找到的大小在intchar等,我想知道去大小样a字符串对象在Python中,整数等。

相关问题:有多少个字节单元中以Python列表(元组)?

我使用XML文件包含指定的尺寸是这场大小的值。我必须做我的这个XML解析和编码。当我想改变一个特定值的现场检查,我想那场值的大小。在这里我想把这个新的价值我公进入是有相同的大小作为XML。我需要检查新值的大小。在的情况下我可以说它的字符串的长度。但在下一个浮动等混淆。


只需使用在sys模块中定义的sys.getsizeof函数。

sys.getsizeof(object[, default]):

Return the size of an object in bytes.
The object can be any type of object.
All built-in objects will return
correct results, but this does not
have to hold true for third-party
extensions as it is implementation
specific.

The default argument allows to define
a value which will be returned if the
object type does not provide means to
retrieve the size and would cause a
TypeError.

getsizeof calls the object’s
__sizeof__ method and adds an additional garbage collector overhead
if the object is managed by the
garbage collector.

使用示例,在python 3.0中:

1
2
3
4
5
6
7
8
9
10
>>> 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,那么您可以使用这个扩展模块。但从未使用过。


How do I determine the size of an object in Python?

答案"just use sys.getsizeof"不是完整的答案。好的。

这个答案确实适用于直接内置的对象,但它不考虑这些对象可能包含什么,特别是包含什么类型,如自定义对象、元组、列表、dict和集合。它们可以彼此包含实例,以及数字、字符串和其他对象。好的。更完整的答案

使用来自anaconda发行版的64位python 3.6,使用sys.getsizeof,我已经确定了以下对象的最小大小,并注意设置和dict预分配空间,使空的对象在设置好的数量之后才会再次增长(这可能因语言的实现而有所不同):好的。

Python 3:好的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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个字节,没有数据的实例几乎没有意义。好的。

此外,每个函数定义都有代码对象,可能是docstrings,以及其他可能的属性,甚至是__dict__。好的。

python 2.7分析,与guppy.hpysys.getsizeof确认:好的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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中的紧凑表示好的。

我认为在64位机器上,每增加一个要引用的项目8个字节是很有意义的。这8个字节指向包含项所在的内存中的位置。在python 2中,4个字节是unicode的固定宽度,如果我没记错的话,但是在python 3中,str变成了宽度等于字符最大宽度的unicode。好的。

(有关插槽的更多信息,请参阅此答案)好的。更完整的功能

我们需要一个搜索列表、元组、集合、dict、obj.__dict__obj.__slots__中的元素的函数,以及其他我们可能还没有想到的东西。好的。

我们想依靠gc.get_referents来进行搜索,因为它在C级别工作(使其非常快)。缺点是get-referents可以返回多余的成员,因此我们需要确保不重复计数。好的。

类、模块和函数是单例的——它们在内存中存在一次。我们对它们的尺寸不太感兴趣,因为我们对它们没什么办法——它们是项目的一部分。因此,如果它们被引用,我们将避免计算它们。好的。

我们将使用一个黑名单的类型,所以我们不包括在我们的大小计数整个程序。好的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
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对象的大小,包括大多数内置对象、集合模块中的类型和自定义类型(时隙类型和其他类型)。好的。

这种类型的函数对我们将要计算内存使用量的类型提供了更细粒度的控制,但有可能遗漏类型:好的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
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)

我相当随意地测试了它(我应该联合测试它):好的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
>>> 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

这个实现在类定义和函数定义上进行分解,因为我们不关注它们的所有属性,但是由于它们应该只存在于进程的内存中一次,所以它们的大小实际上并不重要。好的。好啊。


对于numpy数组,getsizeof不起作用-对于我来说,它总是返回40,因为某些原因:

1
2
3
4
from pylab import *
from sys import getsizeof
A = rand(10)
B = rand(10000)

然后(在伊普利顿):

1
2
3
4
5
In [64]: getsizeof(A)
Out[64]: 40

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

不过,令人高兴的是:

1
2
3
4
5
In [66]: A.nbytes
Out[66]: 80

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


pympler包的asizeof模块可以做到这一点。

用途如下:

1
2
from pympler import asizeof
asizeof.asizeof(my_object)

sys.getsizeof不同,它适用于您自己创建的对象。它甚至可以和numpy一起工作。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
>>> 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

如上所述,

The (byte)code size of objects like classes, functions, methods, modules, etc. can be included by setting option code=True.

如果您需要其他实时数据视图,请使用

module muppy is used for on-line monitoring of a Python application
and module Class Tracker provides off-line analysis of the lifetime of
selected Python objects.


这可能比看起来更复杂,这取决于你想如何计算事物。例如,如果您有一个ints列表,您想要包含对ints引用的列表的大小吗?(即仅列出,不包括其中包含的内容),还是要包括指向的实际数据,在这种情况下,需要处理重复引用,以及当两个对象包含对同一对象的引用时如何防止重复计数。

您可能想看看其中一个python内存分析器,比如pysizer,看看它们是否满足您的需求。


下面是我根据前面的答案编写的一个快速脚本,列出所有变量的大小

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


在我自己多次遇到这个问题之后,我编写了一个小函数(受@aron hall's answer的启发)和测试,它可以完成我预期sys.getsizeof要做的工作:

网址:https://github.com/bosswissam/pysize

如果你对后台感兴趣,给你

编辑:附加下面的代码以便于参考。要查看最新的代码,请检查Github链接。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
    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

正如Raymond Hettinger在这里宣布的那样,python 3.8(2019年第1季度)将改变sys.getsizeof的一些结果:

Python containers are 8 bytes smaller on 64-bit builds.

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

这是在33597版和Inada Naoki(methane)围绕紧凑型Pygc-Head和PR 7043的工作之后发布的。

This idea reduces PyGC_Head size to two words.

Currently, PyGC_Head takes three words; gc_prev, gc_next, and gc_refcnt.

  • gc_refcnt is used when collecting, for trial deletion.
  • gc_prev is used for tracking and untracking.

So if we can avoid tracking/untracking while trial deletion, gc_prev and gc_refcnt can share same memory space.

见提交D5C875B:

Removed one Py_ssize_t member from PyGC_Head.
All GC tracked objects (e.g. tuple, list, dict) size is reduced 4 or 8 bytes.


如果您不需要对象的确切大小,但大致知道它有多大,一种快速(脏)的方法是让程序运行,长时间睡眠,并通过这个特定的python进程检查内存使用情况(例如:mac的活动监视器)。当您试图在一个Python进程中查找单个大型对象的大小时,这是有效的。例如,我最近想检查一个新数据结构的内存使用情况,并将其与Python的set data结构进行比较。首先,我将元素(来自大型公共领域书籍的单词)写入一个集合,然后检查流程的大小,然后对其他数据结构执行相同的操作。我发现带有集合的python进程占用的内存是新数据结构的两倍。同样,您不能准确地说进程使用的内存等于对象的大小。当对象的大小变大时,与您试图监视的对象的大小相比,当进程的其余部分消耗的内存变得微不足道时,这将变得非常接近。


第一:答案。

1
2
3
4
5
import sys

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

讨论:在Python中,您不能访问"直接"内存地址。那么,为什么您需要或想要知道给定对象占用了多少这样的地址呢??这是一个在抽象层次上完全不合适的问题。当你在粉刷你的房子时,你不会问光被油漆中的每一个组成原子吸收或反射的频率是多少,你只会问它是什么颜色——产生这种颜色的物理特性的细节就在这一点旁边。同样,给定的python对象占用的内存字节数也在点的旁边。

那么,为什么要使用python来编写C代码呢?:)