__sizeof__ str大于__sizeof__包含该字符串的元组

时间:2016-11-22 16:48:59

标签: python string python-3.x tuples python-internals

以下代码生成给定的输出。

import sys

print('ex1:')
ex1 = 'Hello'
print('\t', ex1.__sizeof__())

print('\nex2:')
ex2 = ('Hello', 53)
print('\t', ex2.__sizeof__())

输出:

ex1:
     54    
ex2:
     40

为什么__sizeof__()在考虑第二个元素时会打印较小的结果?输出不应该更大吗?我从this answer意识到我应该使用sys.getsizeof(),但这种行为似乎很奇怪。我使用Python 3.5.2

此外,正如@Herbert指出的那样,'Hello'占用的内存比('Hello',)占用的内存更多,这是tuple。这是为什么?

1 个答案:

答案 0 :(得分:13)

这是因为-cp个对象(我非常确定除了字符串之外的所有容器)通过包含各自的实际大小来评估其大小而不是内容,但相反,通过计算指向tuple的指针的大小乘以它们包含的元素。也就是说,它们指向包含的(通用)PyObject指针,以及它对整体大小的贡献。

这在[{3}}手册中暗示:

  

某些对象包含引用到其他对象;这些被称为容器。容器的示例是元组,列表和字典。引用是容器值的一部分。

(我强调引用一词。)

Data Model chapter of the Python Reference,一个包含PyObject类型信息的结构,我们看到In PyTupleType字段的值为tuple

sizeof(PyObject *)

PyTypeObject PyTuple_Type = { PyVarObject_HEAD_INIT(&PyType_Type, 0) "tuple", sizeof(PyTupleObject) - sizeof(PyObject *), sizeof(PyObject *), // <-- sizeof pointer to PyObject's 位构建和Python的32位构建具有等于8个字节的64

这是将乘以sizeof(PyObject *)实例中包含的项目数的值。当我们查看tp_itemsize tuple __sizeof__从[{1}}继承的tuple方法(检查object)时,我们会清楚地看到这一点:

object.__sizeof__ is tuple.__sizeof__

了解static PyObject * object_sizeof(PyObject *self, PyObject *args) { Py_ssize_t res, isize; res = 0; isize = self->ob_type->tp_itemsize; if (isize > 0) res = Py_SIZE(self) * isize; // <-- num_elements * tp_itemsize res += self->ob_type->tp_basicsize; return PyLong_FromSsize_t(res); } (从isize获得)乘以object_size的方式,这是另一个抓取tp_itemsize的宏,表示元素数量ob_size

这就是为什么,即使我们在元组实例中创建一个稍大的字符串:

tuple

其中的元素大小为:

t = ("Hello" * 2 ** 10,)

元组实例的大小:

t[0].__sizeof__()         # 5169

等于内部只有t.__sizeof__() # 32 的那个:

"Hello"

对于字符串,每个单独的字符都会增加从t2 = ("Hello",) t[0].__sizeof__() # 54 t2.__sizeof__() # 32 Tuple size stays the same. 返回的值。这一点,以及str.__sizeof__仅存储指针的事实,给出了一种误导性的印象:tuple的大小比包含它的元组大。

为了完整起见,Py_SIZE(self)是计算这一点的人。它实际上只是将字符串的长度与字符大小相乘(这取决于字符的类型"Hello"12字节字符。

我唯一没有使用元组的原因是它的基本大小(由4表示)被列为tb_basicsize。这会从返回的整体大小中减去sizeof(PyTupleObject) - sizeof(PyObject *)个字节;我还没有找到任何解释(还)。