rba*_*dar 3 python tuples python-2.7 python-3.x
在回答与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
Run Code Online (Sandbox Code Playgroud)
正如您所看到的,空元组与单项元组之间存在很大差异(更准确地说是 2.3 倍)。你知道这里发生了什么吗?由于元组是递归数据结构,我假设sys.getsizeof()返回的值是元组对象本身占用的内存量加上对其包含的对象的引用(如果元组将元素存储为引用而不是值 - 我不知道)。我最初的想法是,像字典一样,元组是在内存中创建的,具有一定的默认大小,可以容纳几个元素。我忘记了空字典保留内存的确切元素数量,但这就是我的一个小例子的意思:
>> sys.getsizeof({})
280
>> sys.getsizeof({"a":0})
280
>> sys.getsizeof({"a":0, "b":1})
280
Run Code Online (Sandbox Code Playgroud)
然而,查看元组似乎并没有表现出相同的行为,因为元组在第一个元素添加到较小的尺寸后会收缩,然后随着添加到其中的每个元素而增长(如预期)。除此之外,列表似乎没有遭受相同的行为:
>> sys.getsizeof([])
72
>> sys.getsizeof([1])
80
>> sys.getsizeof([1,2])
88
Run Code Online (Sandbox Code Playgroud)
空列表在内存方面比具有 1 个或多个元素的列表小 - 完全正常。
我的第二个想法是,单元素元组以某种方式转换为它包含的单个对象,并且所有内容都以某种方式包装,以便看起来它实际上是一个列表(这就是len()工作的原因)。例子:
>> sys.getsizeof((1))
24
>> sys.getsizeof(1)
24
Run Code Online (Sandbox Code Playgroud)
这似乎是可能的,但我不相信它真的发生了。
代码(1)不是元组!Python只会将括号视为表达操作优先级的一种方式。如果您想要一个元组,则其中必须至少有一个逗号。
>>> type(1)
<type 'int'>
>>> type((1))
<type 'int'>
>>> type((1,))
<type 'tuple'>
Run Code Online (Sandbox Code Playgroud)
那么元组的大小将是其中元素数量的线性函数。
>>> sys.getsizeof(tuple())
28
>>> sys.getsizeof((1,))
32
>>> sys.getsizeof((1,2))
36
>>> sys.getsizeof((1,2,3))
40
Run Code Online (Sandbox Code Playgroud)
您没有创建单项元组。任何表达式都可以用括号括起来,而不会改变其含义,因此(1)与 相同1。要创建单个元素元组,请编写(1,). 当你这样做时,内存消耗应该变得更加合理。
但整数怎么可能需要 24 个字节呢?整数通常最多不是8个字节吗?
在Python 3中,所有整数都是任意精度的,甚至在Python 2中,包括整数在内的所有对象对于引用计数、类型信息和其他元数据都有一定的开销。对于一个成熟的 Python 对象来说,24 个字节实际上是相当合理的。
(从技术上讲,小整数是被保留的,因此在这种特殊情况下引用计数可以说是不必要的,但删除它会使解释器的其他部分变得复杂,而不会带来任何显着的好处。)
在回答的同时,我还想澄清一些事情:
(如果元组将元素存储为引用而不是值 - 我不知道)
一般来说,Python 不允许 Python 对象实际上相互包含。在C源代码中,你会看到很多PyObject*变量,但没有PyObject变量。Python 对象存在于堆上,彼此完全独立。换句话说,是的,元组存储的是引用,而不是实际的对象。
我最初的想法是,就像字典一样,元组是在内存中创建的,具有一定的默认大小,可以容纳几个元素。
没有理由这样做。元组有固定的大小。按照官方说法,它们是不可变的,创建后不会改变。非官方地,它们可以在 C 级别进行更改,但这些更改不应该对 Python 代码可见;在创建元组之后但在将其用于任何用途之前,您可以对其进行一次修改,此后就不再修改它。此外,这些改变实际上并没有改变大小,它们只是填充已经存在的数组槽。
由于这种不变性,元组没有理由存储比实际拥有的更多的元素。它们不需要支持快速追加或其他大小更改操作。
| 归档时间: |
|
| 查看次数: |
2065 次 |
| 最近记录: |