我想知道如何在 Python 中获取对象的大小,例如字符串,整数等。
相关问题: Python 列表(元组)中每个元素有多少个字节?
我正在使用一个 XML 文件,其中包含指定值大小的大小字段。我必须解析此 XML 并进行编码。当我想更改特定字段的值时,我将检查该值的大小字段。在这里,我想比较输入的新值是否与 XML 中的值相同。我需要检查新值的大小。如果是字符串,我可以说它的长度。但是如果是 int,float 等,我会感到困惑。
只需使用sys
模块中定义的 sys.getsizeof函数即可。
sys.getsizeof(object[, default])
:返回对象的大小(以字节为单位)。该对象可以是任何类型的对象。所有内置对象都将返回正确的结果,但是对于第三方扩展,这不一定成立,因为它是特定于实现的。
仅考虑直接归因于对象的内存消耗,而不考虑它所引用的对象的内存消耗。
default
参数允许定义一个值,如果对象类型不提供检索大小的方法并会导致TypeError
话,将返回该值。
getsizeof
调用对象的__sizeof__
方法并添加额外的垃圾收集器开销。
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
” 不是一个完整的答案。
该答案确实直接适用于内置对象,但没有考虑这些对象可能包含的内容,特别是不包含哪些类型,例如自定义对象,元组,列表,字典和集合所包含的类型。它们可以互相包含实例,以及数字,字符串和其他对象。
使用 Anaconda 发行版中的 64 位 Python 3.6 和sys.getsizeof
,我确定了以下对象的最小大小,并请注意 set 和 dict 预分配了空间,因此空的对象直到设置一定数量后才会再次增长(可能会因语言的实施而异):
的 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__
。
还要注意,我们使用sys.getsizeof()
是因为我们关心的是边际空间的使用,其中包括来自 docs的对象的垃圾回收开销:
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 中得到了更紧凑的表示形式
我认为在 64 位计算机上,每个附加项目要引用 8 个字节是很有意义的。这 8 个字节指向所包含项在内存中的位置。如果我没记错的话,Python 2 的 unicode 的 4 个字节是固定宽度的,但是在 Python 3 中,str 变成的 unicode 的宽度等于字符的最大宽度。
有关插槽的更多信息,请参见此答案。
我们需要一个函数来搜索列表,元组,集合,字典, 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)
还将使我们避免计算重复项,这将在下一部分中进行。黑名单解决方案会跳过对全部为字符串的键的计数。
为了亲自涵盖其中的大多数类型,我编写了此递归函数以尝试估算大多数 Python 对象的大小,包括大多数内建函数,collections 模块中的类型以及自定义类型(有槽或其他类型) gc
。
这种函数可以对要计算内存使用情况的类型进行更细粒度的控制,但存在将重要类型遗漏的危险:
import sys
from numbers import Number
from collections import deque
from collections.abc import Set, Mapping
ZERO_DEPTH_BASES = (str, bytes, Number, range, bytearray)
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, 'items'):
size += sum(inner(k) + inner(v) for k, v in getattr(obj, 'items')())
# 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软件包的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
如前所述,
如果您需要其他有关实时数据的视图,Pympler 的
模块
muppy
用于对 Python 应用程序进行在线监视,而模块Class Tracker
可对选定的 Python 对象的生存期进行离线分析。