【问题标题】:Python - tuples and memory managementPython - 元组和内存管理
【发布时间】:2015-09-20 00:50:50
【问题描述】:

在回答一个与 Python 相关的问题时,我做了一些实验,发现了一个我无法弄清楚的与元组相关的事情:我很难理解为什么一个空元组比一个包含单个元素的元组占用更多空间(根据sys.getsizeof())。下面的代码是在 64 位 Debian Jessie 系统上使用 Python 2.7.9 的上游版本执行的(也使用 Python 3.4.2 进行了测试,其中值略有不同,但我所说的整体行为仍然存在):

>> sys.getsizeof(())
56
>> sys.getsizeof((1))
24
>> sys.getsizeof((1,2))
72

如您所见,空元组与单项元组存在很大差异(更准确地说是因子 2.3)。有什么想法吗?由于元组是递归数据结构,我假设sys.getsizeof() 返回的值是元组对象本身占用的内存量加上对它包含的对象的引用(如果元组将元素存储为引用而不是值 - 我不'不知道)。我最初的想法是,像字典一样的元组是在内存中创建的,具有一定的默认大小,可以容纳几个元素。我忘记了空字典为其保留内存的元素的确切数量,但这是我的意思,举个小例子:

>> sys.getsizeof({})
280
>> sys.getsizeof({"a":0})
280
>> sys.getsizeof({"a":0, "b":1})
280

但是,查看元组似乎并没有表现出相同的行为,因为元组在第一个元素添加到较小的大小后会缩小,然后随着每个元素的添加而增长(如预期的那样)。除此之外,列表似乎并没有遭受相同的行为:

>> sys.getsizeof([])
72
>> sys.getsizeof([1])
80
>> sys.getsizeof([1,2])
88

空列表在内存方面比具有 1 个或多个元素的列表小 - 完全正常。

我的第二个想法是单元素元组以某种方式转换为它包含的单个对象,并且所有这些都以某种方式包装,因此它看起来实际上是一个列表(这就是 len() 起作用的原因)。示例:

>> sys.getsizeof((1))
24
>> sys.getsizeof(1)
24

这似乎是可能的,但我不相信它真的会发生。

【问题讨论】:

  • (1) 不是元组,它是一个整数,恰好被括在(语义上无意义的)括号中。你想要(1,)
  • @Kevin 您应该补充说作为答案,这肯定是导致他经历的原因。刚刚在我的系统上做了一个快速测试,一个空元组需要 28 个字节的内存,加上每个元素 4 个字节。即,一个 1 元素元组需要 32 个字节。

标签: python python-2.7 python-3.x tuples


【解决方案1】:

您不是在创建单项元组。任何表达式都可以用括号括起来而不改变其含义,因此(1)1 相同。要创建单个元素元组,请编写 (1,)。当你这样做时,内存消耗应该变得更加合理。

但是一个整数怎么可能需要 24 个字节呢?整数通常不是最多8个字节吗?

在 Python 3 中,所有整数都是任意精度的,即使在 Python 2 中,包括整数在内的所有对象对于引用计数、类型信息和其他元数据都有一定的开销。对于成熟的 Python 对象来说,24 字节实际上是相当合理的。

(从技术上讲,小整数是被保留的,因此在这种特殊情况下,引用计数可以说是不必要的,但删除它会使解释器的其他部分复杂化而没有显着的好处。)

在回答的同时,我还想澄清一些事情:

(如果元组将元素存储为引用而不是值 - 我不知道)

一般来说,Python 不允许 Python 对象实际包含彼此。在 C 源代码中,您会看到很多 PyObject* 变量,但没有 PyObject 变量。 Python 对象存在于堆上,彼此完全独立。换句话说,是的,元组存储的是引用,而不是实际的对象。

我最初的想法是,像字典一样的元组是在内存中创建的,具有一定的默认大小,可以容纳几个元素。

没有理由这样做。元组具有固定的大小。正式地,它们是不可变的,并且在创建后不会更改。非正式地,它们可以在 C 级别进行更改,但这些更改不应该对 Python 代码可见;您在创建元组之后修改元组一次,但在您将其用于任何事情之前,此后您不再修改它。此外,这些更改实际上并没有改变大小,它们只是填充了已经存在的数组槽。

由于这种不变性,元组没有理由存储比它们实际拥有的更多的元素。它们不需要支持快速追加或其他改变大小的操作。

【讨论】:

  • 啊,该死的,我忘了用逗号来制作一个元组:D 感谢您的回答!至于整数 - 我知道 Python 上没有原始类型,甚至整数都是对象,因此提到了开销,因此我没有特别询问单个元素的大小。
【解决方案2】:

代码(1) 不是元组! Python 只会将括号视为表达操作优先级的一种方式。如果你想要一个元组,它必须至少有一个逗号。

>>> type(1)
<type 'int'>
>>> type((1))
<type 'int'>
>>> type((1,))
<type 'tuple'>

那么元组的大小将是其中元素数量的线性函数。

>>> sys.getsizeof(tuple())
28
>>> sys.getsizeof((1,))
32
>>> sys.getsizeof((1,2))
36
>>> sys.getsizeof((1,2,3))
40

【讨论】:

    猜你喜欢
    • 2023-03-15
    • 2022-01-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-01-08
    • 1970-01-01
    • 2011-01-14
    • 2016-10-12
    相关资源
    最近更新 更多