Это связано с тем, что tuple
объекты (и я уверен, что все контейнеры, за исключением из строки) оценить их размер не, включив фактические размеры их соответствующего содержания, но, скорее, путем вычисления размера указатели на PyObject
с элементами, которые они содержат. То есть они содержат указатели на (общий) 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
s наследуют от 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
s хранит только указатели, дает неверное впечатление, что "Hello"
имеет больший размер, чем кортеж, содержащий его.
Только для полноты, unicode__sizeof__
- это тот, который вычисляет это. Он действительно просто умножает длину строки с размером символа (который зависит от того, какой тип имеет символ 1
, 2
и 4
байт).
Единственное, что я не получаю с кортежами, это то, почему это базовый размер (обозначается tb_basicsize
) указан как sizeof(PyTupleObject) - sizeof(PyObject *)
. Это отбрасывает 8
байт из общего размера, возвращенного; Я не нашел никаких объяснений этому (пока).
Ваш первый объект не является кортежем, это строка в круглых скобках. – Kasramvd