【问题标题】:Approximately how much memory would a list of 80000 items consume in python?在 python 中,包含 80000 个项目的列表大约会消耗多少内存?
【发布时间】:2013-01-05 00:30:06
【问题描述】:

我有一个 python 列表,其中包含 80000 个列表。这些内部列表中的每一个或多或少都具有这种格式:

["012345", "MYNAME" "Mon", "A", 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20]

你能告诉我这个由 80000 个列表组成的列表大约会消耗多少内存吗?

在 python 中使用和操作这么大的列表是否常见/可以?我做的大部分操作都是用列表推导方法从这个列表中提取数据。

实际上,我想了解的是:python 是否足够快,可以使用列表理解方法从大列表中提取数据。我希望我的脚本快

【问题讨论】:

  • 你的记忆真的有问题吗?大列表应该没问题,但这取决于您要做什么。就目前而言,这里没有真正的问题。
  • 我想了解的是:python 是否足够快,可以使用列表理解方法从大列表中提取数据。我希望我的脚本快。
  • 定义“快速”。定义您想要执行的操作。定义什么是可接受的。你试过什么?您遇到过我们可以帮助您解决的问题吗?
  • 这里重要的是提取。您在这里有 10-12 MB 的数据,这是花生。然而,您可以通过不正确的过滤轻松地破坏性能。用timeit 做你自己的测试,我们不能告诉你事情是否会很快。

标签: python performance list memory


【解决方案1】:
In [39]: lis=["012345", "MYNAME" "Mon", "A", 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20,
     20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20]

In [40]: k=[lis[:] for _ in xrange(80000)]

In [41]: k.__sizeof__()
Out[41]: 325664

In [42]: sys.getsizeof(k)  #after gc_head
Out[42]: 325676

根据sysmodule.c 中的代码,它看起来像是调用__sizeof__ 方法来获取对象的大小。

   837   method = _PyObject_LookupSpecial(o, &PyId___sizeof__);   
   838     if (method == NULL) {
   839         if (!PyErr_Occurred())
   840             PyErr_Format(PyExc_TypeError,
   841                          "Type %.100s doesn't define __sizeof__",
   842                          Py_TYPE(o)->tp_name);
   843     }
   844     else {
   845         res = PyObject_CallFunctionObjArgs(method, NULL);
   846         Py_DECREF(method);
   847     }

然后向它添加一些gc 开销:

   860     /* add gc_head size */
   861     if (PyObject_IS_GC(o)) {
   862         PyObject *tmp = res;
   863         res = PyNumber_Add(tmp, gc_head_size);
   864         Py_DECREF(tmp);
   865     }
   866     return res;
   867 }

我们也可以按照docs 的建议使用recursive sizeof recipe 递归计算每个容器的大小:

In [17]: total_size(k)  #from recursive sizeof recipe
Out[17]: 13125767

In [18]: sum(y.__sizeof__() for x in k for y in x)
Out[18]: 34160000

【讨论】:

  • @alwbtc 1 MB = 220 字节; 12800000./220=12.20703125.
  • 我的服务器内存超过96GB,我觉得没问题。
  • -1:首先请注意,这取决于操作系统(可能还有 Python 的版本)。在我的相同代码中,大小为328。其次,它不一定是线性扩展的。试试sys.getsizeof([lis[:] for _ in xrange(80000)])
  • @alwbtc 12800000.0/(1024*1024 == 12.20703125 MB
  • N 个列表的列表很可能与 N 个独立列表的大小不同。
【解决方案2】:

在我使用 32 位 Python 2.7.3 的机器上,包含您问题中确切列表的 80K 副本的列表大约需要 10MB。这是通过比较两个在其他方面相同的解释器的内存占用量来衡量的,一个有列表,一个没有。

我尝试使用sys.getsizeof() 测量尺寸,但返回的结果明显不正确:

>>> l=[["012345", "MYNAME" "Mon", "A", 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20] for i in range(80000)]
>>> sys.getsizeof(l)
325680

【讨论】:

  • 我认为getsizeof 不会递归。外部列表列表仅存储指针,因此不是很大。您还想添加包含列表的大小(80k * 第一个的大小)。
  • @MartijnPieters:一定是这样的。
【解决方案3】:

sys.getsizeof:(对象,默认)
│ │ getsizeof(object, default) -> int
│ │
│ │ 以字节为单位返回对象的大小。

代码

>> import sys
>> sys.getsizeof(["012345", "MYNAME" "Mon", "A", 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20])
>> 160

它为您的列表返回 160 字节。将其乘以大约 80,000 或 12.8 MB。 (32位机器,Python 2.7.2,Python 3.2)

【讨论】:

    【解决方案4】:

    注意以下与解释器的交互:

    >>> import sys
    >>> array = ['this', 'is', 'a', 'string', 'array']
    >>> sys.getsizeof(array)
    56
    >>> list(map(sys.getsizeof, array))
    [29, 27, 26, 31, 30]
    >>> sys.getsizeof(array) + sum(map(sys.getsizeof, array))
    199
    >>> 
    

    在这种特定情况下的答案是使用sys.getsizeof(array) + sum(map(sys.getsizeof, array)) 来查找字符串列表的大小。但是,以下将是一个更完整的实现,它考虑了对象容器、类和 __slots__ 的用法。

    import sys
    
    def sizeof(obj):
        return _sizeof(obj, set())
    
    def _sizeof(obj, memo):
        # Add this object's size just once.
        location = id(obj)
        if location in memo:
            return 0
        memo.add(location)
        total = sys.getsizeof(obj)
        # Look for any class instance data.
        try:
            obj = vars(obj)
        except TypeError:
            pass
        # Handle containers holding objects.
        if isinstance(obj, (tuple, list, frozenset, set)):
            for item in obj:
                total += _sizeof(item, memo)
        # Handle the two-sided nature of dicts.
        elif isinstance(obj, dict):
            for key, value in dict.items():
                total += _sizeof(key, memo) + _sizeof(value, memo)
        # Handle class instances using __slots__.
        elif hasattr(obj, '__slots__'):
            for key, value in ((name, getattr(obj, name))
                for name in obj.__slots__ if hasattr(obj, name)):
                total += _sizeof(key, memo) + _sizeof(value, memo)
        return total
    

    编辑:

    在解决这个问题一段时间后,设计了以下替代方案。请注意,它不适用于无限迭代器。此代码最适合用于分析的静态数据结构。

    import sys
    
    sizeof = lambda obj: sum(map(sys.getsizeof, explore(obj, set())))
    
    def explore(obj, memo):
        loc = id(obj)
        if loc not in memo:
            memo.add(loc)
            yield obj
            # Handle instances with slots.
            try:
                slots = obj.__slots__
            except AttributeError:
                pass
            else:
                for name in slots:
                    try:
                        attr = getattr(obj, name)
                    except AttributeError:
                        pass
                    else:
                        yield from explore(attr, memo)
            # Handle instances with dict.
            try:
                attrs = obj.__dict__
            except AttributeError:
                pass
            else:
                yield from explore(attrs, memo)
            # Handle dicts or iterables.
            for name in 'keys', 'values', '__iter__':
                try:
                    attr = getattr(obj, name)
                except AttributeError:
                    pass
                else:
                    for item in attr():
                        yield from explore(item, memo)
    

    【讨论】:

      【解决方案5】:

      应用Size of Python objects (revised) 配方中的当前(第 13 版)代码并放置在名为 sizeof 的模块中,然后将其应用到您的示例列表中,结果如下(使用 32 位 Python 2.7.3):

      from sizeof import asizeof  # from http://code.activestate.com/recipes/546530
      
      MB = 1024*1024
      COPIES = 80000
      lis=["012345", "MYNAME" "Mon", "A", 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20,
           20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20]
      
      lis_size = asizeof(lis)
      print 'asizeof(lis): {} bytes'.format(lis_size)
      list_of_lis_size = asizeof([lis[:] for _ in xrange(COPIES)])
      print 'asizeof(list of {:,d} copies of lis): {:,d} bytes ({:.2f} MB)'.format(
                               COPIES, list_of_lis_size, list_of_lis_size/float(MB))
      
      asizeof(lis): 272 bytes
      asizeof(list of 80,000 copies of lis): 13,765,784 bytes (13.13 MB)
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2013-08-12
        • 2019-05-05
        • 2012-10-25
        • 1970-01-01
        • 1970-01-01
        • 2012-09-11
        • 1970-01-01
        • 2017-02-16
        相关资源
        最近更新 更多