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

时间:2009-01-16 05:07:13

标签: python object memory memory-management sizeof

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

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

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

15 个答案:

答案 0 :(得分:534)

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

  

sys.getsizeof(object[, default])

     

以字节为单位返回对象的大小。   对象可以是任何类型的对象。   所有内置对象都将返回   正确的结果,但事实并非如此   必须适用于第三方   扩展,因为它是实现   具体

     

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代替。从来没有用过它。

答案 1 :(得分:257)

  

如何在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

更完整的功能

我们想要一个搜索列表,元组,集合,词组,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)还可以避免计算重复项,我们将在下一节中进行计算。黑名单解决方案会跳过完全符合字符串的计数键。

白名单类型,递归访问者(旧实现)

为了自己覆盖大多数这些类型,我不是依赖于gc模块,而是编写了这个递归函数来尝试估计大多数Python对象的大小,包括大多数内置函数,集合模块中的类型和自定义类型(slotted)否则)。

这种函数可以对我们计算内存使用量的类型进行更细粒度的控制,但是存在将类型排除在外的危险:

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

这个实现分解了类定义和函数定义,因为我们不会追求它们的所有属性,但由于它们应该只存在于内存中一次,因此它们的大小确实也不重要得多。

答案 2 :(得分:77)

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

答案 3 :(得分:59)

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

作为mentioned

  

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

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

  

module muppy用于在线监控Python应用程序   和模块Class Tracker提供了生命周期的离线分析   选择的Python对象。

答案 4 :(得分:12)

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

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

答案 5 :(得分:8)

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

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

答案 6 :(得分:8)

我自己多次遇到这个问题,我写了一个小功能(灵感来自@ 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

答案 7 :(得分:4)

Python 3.8(2019年第一季度)将更改sys.getsizeof的某些结果,就像Raymond Hettinger的announced here一样:

  

在64位版本中,Python容器要小8字节。

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

这是在issue 33597Inada Naoki (methane)围绕Compact PyGC_Head和PR 7043

工作之后
  

此想法将PyGC_Head的大小减小到两个单词

     

当前,PyGC_Head用了三个字gc_prevgc_nextgc_refcnt

     
      
  • gc_refcnt用于收集,用于试验删除。
  •   
  • gc_prev用于跟踪和取消跟踪。
  •   
     

因此,如果我们在试用删除时可以避免跟踪/取消跟踪,则gc_prevgc_refcnt可以共享相同的内存空间。

请参见commit d5c875b

  

Py_ssize_t中删除了一位PyGC_Head成员。
  所有GC跟踪的对象(例如元组,列表,字典)的大小都减少了4或8个字节。

答案 8 :(得分:2)

如果不想包含链接(嵌套)对象的大小,请使用sys.getsizeof()

但是,如果您要对嵌套在列表,字典,集合,元组中的子对象进行计数(通常这就是您要查找的内容),请使用递归 deep 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

您还可以在漂亮工具箱中找到此功能,以及许多其他有用的单行代码:

https://github.com/mwojnars/nifty/blob/master/util.py

答案 9 :(得分:2)

您可以序列化对象以得出与对象大小紧密相关的度量:

import pickle

## let o be the object, whose size you want to measure
size_estimate = len(pickle.dumps(o))

答案 10 :(得分:0)

如果您不需要对象的确切大小,但是大致知道对象的大小,一种快速(又脏)的方法是让程序运行,睡眠较长时间并检查内存特定python进程的使用情况(例如:Mac的活动监视器)。当您尝试在python进程中查找单个大对象的大小时,这将是有效的。例如,我最近想检查新数据结构的内存使用情况,并将其与Python的set数据结构进行比较。首先,我将元素(大型公共领域书中的单词)写到一个集合中,然后检查过程的大小,然后对其他数据结构执行相同的操作。我发现一组Python进程占用的内存是新数据结构的两倍。同样,您将无法确切地说出进程使用的内存等于对象的大小。随着对象的大小变大,与您尝试监视的对象的大小相比,该过程的其余部分所消耗的内存可以忽略不计。

答案 11 :(得分:0)

您可以使用如下所述的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")

答案 12 :(得分:0)

我用这个技巧...在小物体上可能不准确,但是我认为对于复杂物体(例如pygame表面)比sys.getsizeof()更为准确

class Field(object):
    """
        Contains the field properties

        Args:
            field_json (dict): dictionary containing field properties 
    """

    def __init__(self, field_json=None):
        self.name = field_json.get("name")
        self.field_type = field_json.get("field_type")
        self.expected_values = field_json.get("expected_values", ["*"])
        self.negative_values = field_json.get("negative_values", ["-", ""])
        self.condition = field_json.get("condition", ["-", ""])
        self.validity = field_json.get("validity", "")

    def __str__(self):
        return self.name

在Windows 10上,python 3.7.3,输出为:

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

答案 13 :(得分:0)

使用以下函数获取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

答案 14 :(得分:-5)

第一:答案。

import sys

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

讨论:
在Python中,您无法访问“直接”内存地址。那么,为什么你需要或想要知道给定对象占用了多少这样的地址?这个抽象层次的问题完全不合适。当你在画你的房子时,你不会问漆的每个组成原子吸收或反射的光的频率,你只要问它是什么颜色 - 产生这种颜色的物理特征的细节与此无关。类似地,给定Python对象占用的内存字节数不在此处。

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