我想知道如何在 Python 中获取字符串、整数等对象的大小。
我正在使用一个 XML 文件,其中包含指定值大小的大小字段。我必须解析这个 XML 并进行编码。当我想更改特定字段的值时,我会检查该值的大小字段。这里我想比较一下我要输入的新值是否与XML中的大小相同。我需要检查新值的大小。如果是字符串,我可以说它的长度。但在 int、float 等情况下,我很困惑。
我想知道如何在 Python 中获取字符串、整数等对象的大小。
我正在使用一个 XML 文件,其中包含指定值大小的大小字段。我必须解析这个 XML 并进行编码。当我想更改特定字段的值时,我会检查该值的大小字段。这里我想比较一下我要输入的新值是否与XML中的大小相同。我需要检查新值的大小。如果是字符串,我可以说它的长度。但在 int、float 等情况下,我很困惑。
只需使用模块sys.getsizeof
中定义的功能。sys
sys.getsizeof(object[, default])
:返回对象的大小(以字节为单位)。对象可以是任何类型的对象。所有内置对象都将返回正确的结果,但这对于第三方扩展不一定适用,因为它是特定于实现的。
仅考虑直接归因于对象的内存消耗,而不考虑它所引用的对象的内存消耗。
该
default
参数允许定义一个值,如果对象类型不提供检索大小的方法并会导致TypeError
.
getsizeof
如果对象由垃圾收集器管理,则调用对象的__sizeof__
方法并增加额外的垃圾收集器开销。有关使用递归查找容器大小及其所有内容的示例,请参见recursive sizeof recipe 。
getsizeof()
使用示例,在 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
,我已经确定了以下对象的最小大小,并注意集合和字典预分配空间,因此空的空间不会再次增长,直到达到设定的数量(可能会有所不同语言的实现):
蟒蛇 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
在类定义中都有一个描述符(如 a )。
开槽实例的第一个元素从 48 个字节开始,每增加 8 个字节。只有空槽对象有 16 个字节,没有数据的实例没有什么意义。
此外,每个函数定义都有代码对象,可能是文档字符串,以及其他可能的属性,甚至是__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
and确认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 个字节指向内存中包含的项目所在的位置。如果我没记错的话,这 4 个字节是 Python 2 中 unicode 的固定宽度,但在 Python 3 中,str 成为宽度等于字符最大宽度的 unicode。
有关插槽的更多信息,请参阅此答案。
我们想要一个函数来搜索列表、元组、集合、字典、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 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
如前所述,_
如果您需要其他关于实时数据的视图,Pymler 的
模块
muppy
用于在线监控 Python 应用程序,模块Class Tracker
提供对选定 Python 对象生命周期的离线分析。
对于 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
您可以序列化对象以导出与对象大小密切相关的度量:
import pickle
## let o be the object whose size you want to measure
size_estimate = len(pickle.dumps(o))
如果您想测量无法腌制的对象(例如由于 lambda 表达式), dill 或 cloudpickle 可以是一个解决方案。
Python 3.8(2019 年第一季度)将改变 的一些结果sys.getsizeof
,正如Raymond Hettinger在此宣布的那样:
Python 容器在 64 位版本上小 8 个字节。
tuple () 48 -> 40
list [] 64 ->56
set() 224 -> 216
dict {} 240 -> 232
这是在issue 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
可以共享相同的内存空间。
请参阅提交 d5c875b:
从 中删除了一名
Py_ssize_t
成员PyGC_Head
。
所有 GC 跟踪的对象(例如 tuple、list、dict)的大小都减少了 4 或 8 个字节。
如果您不想包含链接(嵌套)对象的大小,请使用sys.getsizeof() 。
然而,如果你想计算嵌套在列表、字典、集合、元组中的子对象——通常这就是你要找的——使用递归深度 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
您还可以在漂亮的工具箱中找到此功能,以及许多其他有用的单行代码:
这可能比看起来更复杂,具体取决于您要如何计算事物。例如,如果您有一个整数列表,您是否想要包含对整数的引用的列表的大小?(即仅列出,而不是其中包含的内容),或者您是否要包含指向的实际数据,在这种情况下您需要处理重复引用,以及当两个对象包含对的引用时如何防止重复计算同一个对象。
您可能想查看其中一个 python 内存分析器,例如pysizer,看看它们是否满足您的需求。
我自己多次遇到这个问题后,我编写了一个小函数(受@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
这是我根据之前对所有变量的列表大小的答案编写的快速脚本
for i in dir():
print (i, sys.getsizeof(eval(i)) )
使用以下函数获取 python 对象的实际大小:
import sys
import gc
def actualsize(input_obj):
memory_size = 0
ids = set()
objects = [input_obj]
while objects:
new = []
for obj in objects:
if id(obj) not in ids:
ids.add(id(obj))
memory_size += sys.getsizeof(obj)
new.append(obj)
objects = gc.get_referents(*new)
return memory_size
actualsize([1, 2, [3, 4, 5, 1]])
参考:https ://towardsdatascience.com/the-strange-size-of-python-objects-in-memory-ce87bdfbb97f
如果您不需要对象的确切大小但大致知道它有多大,一种快速(且肮脏)的方法是让程序运行,长时间休眠,并检查内存使用情况(例如: Mac 的活动监视器) 通过这个特定的 python 进程。当您试图在 python 进程中查找单个大对象的大小时,这将是有效的。例如,我最近想检查一个新数据结构的内存使用情况,并将其与 Python 的 set 数据结构的内存使用情况进行比较。首先,我将元素(来自大型公共领域书籍的单词)写入一个集合,然后检查进程的大小,然后对另一个数据结构做同样的事情。我发现带有集合的 Python 进程占用的内存是新数据结构的两倍。再说一次,你不会 不能准确地说进程使用的内存等于对象的大小。随着对象的大小变大,与您尝试监视的对象的大小相比,其余进程消耗的内存变得可以忽略不计,这将变得接近。
我使用这个技巧......可能对小物体不准确,但我认为它对于复杂物体(如 pygame 表面)而不是 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
如果性能不是问题,最简单的解决方案是腌制和测量:
import pickle
data = ...
len(pickle.dumps(data))
您可以使用如下所述的 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")