首页 文章

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

提问于
浏览
472

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

相关问题:How many bytes per element are there in a Python list (tuple)?

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

8 回答

  • 222

    根据您想要计算的内容,这可能比它看起来更复杂 . 例如,如果您有一个整数列表,您是否想要列表的大小包含对int的引用? (即仅列出,而不是包含在其中的内容),或者是否要包含指向的实际数据,在这种情况下,您需要处理重复的引用,以及当两个对象包含对引用的引用时如何防止重复计数同一个对象 .

    您可能需要查看其中一个python内存分析器,例如pysizer,看看它们是否满足您的需求 .

  • -5

    这是我根据以前对所有变量列表大小的答案编写的快速脚本

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

    只需使用 sys 模块中定义的sys.getsizeof函数即可 .

    sys.getsizeof(object [,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并且没有 sys.getsizeof ,则可以使用this extensive module . 从来没有用过它 .

  • 55

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

    答案是“只使用sys.getsizeof”并不是一个完整的答案 .

    该答案直接适用于内置对象,但它不考虑这些对象可能包含的内容,特别是元组,列表,字符串和集合等类型 . 它们可以包含彼此的实例,以及数字,字符串和其他对象 .

    更完整的答案

    使用来自Anaconda发行版的64位Python 3.6,使用sys.getsizeof,我确定了以下对象的最小大小,并注意set和dicts预分配空间,因此空的不会再次增长,直到设定量(可能因语言的实施而有所不同):

    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 ) .

    Slotted实例在其第一个元素上以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.
    

    请注意,词典(but not sets)在Python 3.6中得到more compact representation

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

    (有关插槽的更多信息,see this answer

    递归访问者以获得更完整的功能

    为了覆盖大多数这些类型,我编写了这个递归函数来尝试估计大多数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
    

    它有点打破了类定义和函数定义,因为我没有追求它们的所有属性,但由于它们应该只存在于内存中一次,因此它们的大小确实无关紧要 .

  • 76

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

    Pympler包的 asizeof 模块可以做到这一点 .

    使用方法如下:

    from pympler import asizeof
    asizeof.asizeof(my_object)
    

    sys.getsizeof 不同,它是 works for your self-created objects . 它甚至适用于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
    

    mentioned

    通过设置选项代码= True,可以包含类,函数,方法,模块等对象的(字节)代码大小 .

    如果您需要有关实时数据的其他视图,请参阅Pympler's

    模块muppy用于在线监控Python应用程序和模块类跟踪器提供对所选Python对象的生命周期的离线分析 .

  • 6

    我自己多次遇到这个问题,我写了一个小函数(灵感来自@ aaron-hall的答案)和测试,它做了我期望的sys.getsizeof做的事情:

    https://github.com/bosswissam/pysize

    如果你对背景故事感兴趣,here it is

    编辑:附上以下代码以便于参考 . 要查看最新代码,请查看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
    
  • 12

    第一:答案 .

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

    讨论:
    在Python中,您无法访问"direct"内存地址 . 那么,为什么你需要或想要知道给定对象占用了多少这样的地址?它在这个抽象层次上完全不合适 . 当你询问油漆中每个组成原子吸收或反射的光的频率时,你只需询问它是什么颜色 - 产生这种颜色的物理特征的细节就在这一点之外 . 类似地,给定Python对象占用的内存字节数不在此处 .

    那么,为什么要尝试使用Python编写C代码? :)

相关问题