【问题标题】:Difference between Bytearray and List in PythonPython中Bytearray和List的区别
【发布时间】:2016-01-23 13:22:33
【问题描述】:

我很想知道 Bytearray 和 Python 中的列表之间的内存管理有何不同。

我发现了一些问题,例如Difference between bytearray and list,但没有完全回答我的问题。

正是我的问题...

from array import array
>>> x = array("B", (1,2,3,4))
>>> x.__sizeof__()
36
>>> y = bytearray((1,2,3,4))
>>> y.__sizeof__()
32
>>> z = [1,2,3,4]
>>> z.__sizeof__()
36

正如我们所见,list/array.array(4 个元素为 36 字节)和字节数组(4 个元素为 32 字节)之间存在大小差异。有人可以向我解释为什么会这样吗?对于字节数组来说,它为4 元素( 4 * 8 == 32 ) 占用了32 字节的内存是有意义的,但是对于list 和array.array,如何解释呢?

# Lets take the case of bytearray ( which makes more sense to me at least :p)
for i in y:
        print(i, ": ", id(i))

1 :  499962320
2 :  499962336 #diff is 16 units
3 :  499962352 #diff is 16 units
4 :  499962368 #diff is 16 units

当每个元素只占用8字节时,为什么这里两个连续元素之间的差异相差16个单位。这是否意味着每个内存地址指针都指向一个半字节?

另外,整数的内存分配标准是什么?我读到 Python 会根据整数的值分配更多的内存(如果我错了,请纠正我)就像数字越大内存越多。

例如:

>>> y = 10
>>> y.__sizeof__()
14
>>> y = 1000000
>>> y.__sizeof__()
16
>>> y = 10000000000000
>>> y.__sizeof__()
18

Python分配内存的标准是什么?

为什么 Python 占用这么多内存,而 C 只占用 8 个字节(我的是 64 位机器)?当它们完全在整数(2 ** 64) 的范围内时?

元数据:

Python 版本: '3.4.3 (v3.4.3:9b73f1c3e601, Feb 24 2015, 22:43:06) [MSC v.1600 32 bit (Intel)]'

机器架构: 64 位

P.S:请引导我阅读一篇更好地解释 Python 内存管理的好文章。我花了将近一个小时来弄清楚这些事情,最后在 SO 中提出了这个问题。 :(

【问题讨论】:

  • 好问题,赞成。嘿,你很幸运:在我的 Linux Xubuntu 64 位机器上,CPython 3.4.3 y.__sizeof__() 给我28y=10,同样为y=1M32y=10000000000000
  • 嗨@Pynchia,虽然我的机器是64位,但我的是32位python。我不确定,但这可能是原因。让我们等待有人澄清。

标签: python arrays memory


【解决方案1】:

我并不是说这是完整的答案,但有一些提示可以理解这一点。

bytearray 是字节序列,list 是对象引用序列。所以[1,2,3] 实际上持有指向那些存储在其他地方的内存中的整数的内存指针。要计算列表结构的总内存消耗,我们可以这样做(我在任何地方都使用sys.getsizeof,它调用__sizeof__ 加上GC 开销)

>>> x = [1,2,3]
>>> sum(map(getsizeof, x)) + getsizeof(x)
172

不同机器上的结果可能不同。

另外,看看这个:

>> getsizeof([])
64

这是因为列表是可变的。为了更快,这个结构分配了一些内存range来存储对对象的引用(加上一些存储元数据,例如列表的长度)。当您追加项目时,下一个存储单元将填充对这些项目的引用。当没有空间存储新项目时,分配新的、更大的范围,将现有数据复制到那里并释放旧数据。这称为动态数组。

您可以通过运行此代码来观察此行为。

import sys 
data=[]
n=15
for k in range(n):
    a = len(data)
    b = sys.getsizeof(data)
    print('Length: {0:3d}; Size in bytes: {1:4d}'.format(a, b))
    data.append(None)

我的结果:

Length:   0; Size in bytes:   64 
Length:   1; Size in bytes:   96
Length:   2; Size in bytes:   96 
Length:   3; Size in bytes:   96
Length:   4; Size in bytes:   96 
Length:   5; Size in bytes:  128
Length:   6; Size in bytes:  128 
Length:   7; Size in bytes:  128
Length:   8; Size in bytes:  128 
Length:   9; Size in bytes:  192
Length:  10; Size in bytes:  192 
Length:  11; Size in bytes:  192
Length:  12; Size in bytes:  192 
Length:  13; Size in bytes:  192
Length:  14; Size in bytes:  192

我们可以看到有 64 个字节用于存储 8 个内存地址(每个 64 位)。

bytearray() 几乎相同(将第二行更改为 data = bytearray() 并在最后一行添加 1)。

Length:   0; Size in bytes:   56
Length:   1; Size in bytes:   58
Length:   2; Size in bytes:   61
Length:   3; Size in bytes:   61
Length:   4; Size in bytes:   63
Length:   5; Size in bytes:   63
Length:   6; Size in bytes:   65
Length:   7; Size in bytes:   65
Length:   8; Size in bytes:   68
Length:   9; Size in bytes:   68
Length:  10; Size in bytes:   68
Length:  11; Size in bytes:   74
Length:  12; Size in bytes:   74
Length:  13; Size in bytes:   74
Length:  14; Size in bytes:   74

不同之处在于内存现在用于保存实际字节值,而不是指针。

希望能帮助您进一步调查。

【讨论】:

  • 嗨@anti1869,感谢您的评论。它非常详尽和有用。但我在您的评论中有以下问题。我无法在此处添加所有信息,因此在下方添加了另一条评论。谢谢
  • 按照你的解释理解列表,但是为什么字节数组的大小以56开头。为什么达到74后稳定?如果您能提供更多信息,为什么初始大小为 64 和 56,也将很高兴。谢谢
  • 查看数据结构源代码。在那里,您将看到内部容器结构以及它在初始化时为什么分配内存。还有非常清晰可见的增长算法。 github.com/python/cpython/blob/master/Objects/listobject.c
猜你喜欢
  • 2011-05-04
  • 1970-01-01
  • 2012-09-26
  • 2020-08-20
  • 1970-01-01
  • 2022-01-14
  • 2021-07-24
  • 2016-12-16
相关资源
最近更新 更多