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

自古美人都是妖i 提交于 2020-01-06 17:06:29

【推荐】2019 Java 开发者跳槽指南.pdf(吐血整理) >>>

在C语言中,我们可以找到intchar等的大小。我想知道如何在Python中获取对象的大小,例如字符串,整数等。

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

我使用的XML文件包含指定值大小的大小字段。 我必须解析此XML并进行编码。 当我想更改特定字段的值时,我将检查该值的大小字段。 在这里,我想比较输入的新值是否与XML中的值相同。 我需要检查新值的大小。 如果是字符串,我可以说它的长度。 但是如果是int,float等,我会感到困惑。


#1楼

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

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

#2楼

如何确定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字节

有关函数和类定义的一些警告:

请注意,每个类定义都有一个用于类attrs的代理__dict__ (48字节)结构。 每个插槽在类定义中都有一个描述符(如property )。

开槽实例在其第一个元素上以48个字节开头,并每增加8个字节。 只有空的带槽对象具有16个字节,而没有数据的实例意义不大。

同样,每个函数定义都有代码对象,可能是文档字符串,还有其他可能的属性,甚至是__dict__

Python 2.7分析,已通过guppy.hpysys.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对象的大小,包括大多数内建函数,集合模块中的类型以及自定义类型(有槽或其他类型),而不是依赖于gc模块。 。

这种功能可以对要计算内存使用情况的类型进行更细粒度的控制,但存在将类型排除在外的危险:

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

这种实现方式破坏了类定义和函数定义,因为我们没有使用它们的所有属性,但是由于它们在该进程的内存中应该只存在一次,因此它们的大小实际上并没有太大关系。


#3楼

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

如前所述

可以通过设置option code=True来包括对象(类,函数,方法,模块等)的(字节)代码大小。

如果您需要其他有关实时数据的视图,Pympler的

模块muppy用于对Python应用程序进行在线监视,而模块Class Tracker提供对所选Python对象生命周期的离线分析。


#4楼

对于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

#5楼

我本人多次遇到此问题,然后写了一个小函数(受@ 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
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!