Saya akan mempelajari lebih dalam basis kode CPython sehingga kita dapat melihat bagaimana ukuran sebenarnya dihitung. Dalam contoh spesifik Anda , tidak ada alokasi berlebih yang dilakukan, jadi saya tidak akan menyentuhnya .
Saya akan menggunakan nilai 64-bit di sini, seperti Anda.
Ukuran untuk lists dihitung dari fungsi berikut, list_sizeof:
static PyObject *
list_sizeof(PyListObject *self)
{
Py_ssize_t res;
res = _PyObject_SIZE(Py_TYPE(self)) + self->allocated * sizeof(void*);
return PyInt_FromSsize_t(res);
}
Berikut Py_TYPE(self)adalah makro yang mengambil ob_typedari self(kembali PyList_Type) sementara _PyObject_SIZEadalah makro lain yang mengambil tp_basicsizedari jenis itu. tp_basicsizedihitung sebagai di sizeof(PyListObject)mana PyListObjectstruct instance.
The PyListObjectStruktur memiliki tiga bidang:
PyObject_VAR_HEAD # 24 bytes
PyObject **ob_item; # 8 bytes
Py_ssize_t allocated; # 8 bytes
ini memiliki komentar (yang saya pangkas) menjelaskan apa itu, ikuti tautan di atas untuk membacanya. PyObject_VAR_HEADberkembang menjadi tiga bidang 8 byte ( ob_refcount, ob_typedan ob_size) jadi 24kontribusi byte.
Jadi untuk saat resini adalah:
sizeof(PyListObject) + self->allocated * sizeof(void*)
atau:
40 + self->allocated * sizeof(void*)
Jika contoh daftar memiliki elemen yang dialokasikan. bagian kedua menghitung kontribusinya. self->allocated, seperti yang tersirat dari namanya, menampung jumlah elemen yang dialokasikan.
Tanpa elemen apa pun, ukuran daftar dihitung menjadi:
>>> [].__sizeof__()
40
yaitu ukuran dari instance struct.
tupleobjek tidak mendefinisikan tuple_sizeoffungsi. Sebaliknya, mereka menggunakan object_sizeofuntuk menghitung ukurannya:
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;
res += self->ob_type->tp_basicsize;
return PyInt_FromSsize_t(res);
}
Ini, seperti untuk lists, mengambil tp_basicsizedan, jika objek memiliki non-nol tp_itemsize(artinya memiliki instance dengan panjang variabel), ia mengalikan jumlah item dalam tupel (yang diterimanya Py_SIZE) dengan tp_itemsize.
tp_basicsizelagi menggunakan di sizeof(PyTupleObject)mana PyTupleObjectstruct berisi :
PyObject_VAR_HEAD # 24 bytes
PyObject *ob_item[1]; # 8 bytes
Jadi, tanpa elemen apa pun (yaitu, Py_SIZEmengembalikan 0) ukuran tupel kosong sama dengan sizeof(PyTupleObject):
>>> ().__sizeof__()
24
Hah? Nah, inilah keanehan yang belum saya temukan penjelasannya, tp_basicsizedari tuples sebenarnya dihitung sebagai berikut:
sizeof(PyTupleObject) - sizeof(PyObject *)
mengapa 8byte tambahan dihapus dari tp_basicsizeadalah sesuatu yang saya belum bisa temukan. (Lihat komentar MSeifert untuk penjelasan yang mungkin)
Tapi, pada dasarnya ini adalah perbedaan dalam contoh spesifik Anda . lists juga menyimpan sejumlah elemen yang dialokasikan yang membantu menentukan kapan harus mengalokasikan berlebihan lagi.
Sekarang, ketika elemen tambahan ditambahkan, daftar memang melakukan alokasi berlebih ini untuk mencapai O (1) tambahan. Ini menghasilkan ukuran yang lebih besar karena MSeifert menutupi jawabannya dengan baik.