2016-11-22 14 views
5

以下代码会生成给定输出。__sizeof__ str大于__sizeof__包含该字符串的元组

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

+0

你的第一个对象不是一个元组,它是括号内的一个字符串。 – Kasramvd

回答

13

这是由于这样的事实:tuple对象(我敢肯定,除了从字符串中的所有容器)通过包括各自含量的实际尺寸,而是通过计算尺寸评估其大小指向PyObject s指示它们包含的元素。也就是说,他们持有指向包含(通用)PyObject的指针,这就是它的总体规模。

这在Data Model chapter of the Python Reference手册被暗示:

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

(我强调这个词引用)

In PyTupleType,其中在tuple类型的信息都包含一个结构,我们看到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 

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

这是要乘以tuple实例中包含的项目数量的值。当我们看object_size__sizeof__方法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(从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__是计算这个。它实际上只是将字符串的长度与字符大小相乘(这取决于字符的种类是1,24字节字符)。

我没有得到元组唯一的事情就是为什么它的基本大小(由tb_basicsize表示)被列为sizeof(PyTupleObject) - sizeof(PyObject *)。这从返回的整体大小中删除8字节;我还没有找到任何解释(尚未)。

+1

另一个最好的答案,但我不知道是否在这些python-internals问题上忽略了cpython标签 –

+0

我想知道我自己@Chris_Rands。前一段时间,在浏览标签顶部Q之后,我意识到大多数人将'python-internals'与'CPython'的工作方式等同起来,所以我只是随着这种情绪而摇摆:-) –