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

__sizeof__ str is larger than __sizeof__ a tuple containing that string

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

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。这是为什么?

这是因为 tuple 对象(我很确定所有容器除了字符串)通过包含实际大小来评估它们的大小 而不是 它们各自的内容,而是通过计算指向 PyObjects 的指针的大小乘以它们包含的元素。也就是说,它们持有指向所包含的(通用)PyObject 的指针,这就是影响其整体大小的原因。

这在 Data Model chapter of the Python Reference 手册中有所暗示:

Some objects contain references to other objects; these are called containers. Examples of containers are tuples, lists and dictionaries. The references are part of a container’s value.

(我强调的是参考这个词。)

In PyTupleType, a struct where the information on the tuple type is contained, we see that the tp_itemsize 字段的值是 sizeof(PyObject *)

PyTypeObject PyTuple_Type = {
    PyVarObject_HEAD_INIT(&PyType_Type, 0)
    "tuple",
    sizeof(PyTupleObject) - sizeof(PyObject *),
    sizeof(PyObject *),  // <-- sizeof pointer to PyObject's

32bit 构建和 Python 的 64bit 构建有一个 sizeof(PyObject *) 等于 8 个字节。

这是要乘以 tuple 实例中包含的项目数的值。当我们查看 object_size 时,tuple 继承自 object__sizeof__ 方法(检查 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(从 tp_itemsize 获得)如何乘以 Py_SIZE(self),这是另一个获取 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 仅存储指针这一事实一起,给人一种误导的印象,即 "Hello" 的大小大于包含它的元组。

为了完整起见,unicode__sizeof__ 是计算这个的那个。它实际上只是将字符串的长度乘以字符大小(这取决于字符是 124 字节字符的类型)。

我对元组唯一不明白的是为什么它的基本大小(由 tb_basicsize 表示)被列为 sizeof(PyTupleObject) - sizeof(PyObject *)。这从返回的总大小中减少了 8 字节;我还没有找到任何解释。