Je vais le prendre plus profondément les Disponible de la base de code afin que nous puissions voir comment les tailles sont effectivement calculés. Dans votre exemple, pas de sur-allocation ont été effectuées, donc je ne touche même pas sur que.
Je vais utiliser les valeurs de 64 bits ici, comme vous l'êtes.
La taille pour l' list
s est calculé à partir de la fonction suivante, 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);
}
Ici, Py_TYPE(self)
est une macro qui saisit l' ob_type
de self
(de retour PyList_Type
), tandis que _PyObject_SIZE
est une autre macro qui attrape tp_basicsize
de ce type. tp_basicsize
est calculé comme sizeof(PyListObject)
où PyListObject
, est l'instance struct.
L' PyListObject
structure comporte trois champs:
PyObject_VAR_HEAD # 24 bytes
PyObject **ob_item; # 8 bytes
Py_ssize_t allocated; # 8 bytes
ces commentaires (que j'ai coupé) expliquant ce qu'ils sont, suivez le lien ci-dessus pour les lire. PyObject_VAR_HEAD
se développe en trois 8 octets des champs (ob_refcount
, ob_type
et ob_size
un 24
octet contribution.
Donc pour l'instant, res
est:
sizeof(PyListObject) + self->allocated * sizeof(void*)
ou:
40 + self->allocated * sizeof(void*)
Si l'instance de liste a des éléments qui sont alloués. la deuxième partie calcule leur contribution. self->allocated
, comme son nom l'indique, contient le nombre de allouées éléments.
Sans aucun éléments, la taille des listes est fixée à:
>>> [].__sizeof__()
40
j'.e la taille de l'instance struct.
tuple
objets ne définissent pas un tuple_sizeof
fonction. Au lieu de cela, ils utilisent object_sizeof
pour le calcul de leur taille:
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);
}
Cela, comme pour list
s, saisit l' tp_basicsize
et, si l'objet a une valeur non nulle tp_itemsize
(sens de la longueur variable des cas), il multiplie le nombre d'éléments dans le tuple (qu'il obtient via Py_SIZE
) avec tp_itemsize
.
tp_basicsize
utilise à nouveau sizeof(PyTupleObject)
où l' PyTupleObject
struct contient:
PyObject_VAR_HEAD # 24 bytes
PyObject *ob_item[1]; # 8 bytes
Alors, sans aucun des éléments (c'est - Py_SIZE
retours 0
) de la taille de vide de n-uplets est égale à sizeof(PyTupleObject)
:
>>> ().__sizeof__()
24
hein? Eh bien, voici une bizarrerie que je n'ai pas trouvé une explication, l' tp_basicsize
de tuple
s est en fait calculé comme suit:
sizeof(PyTupleObject) - sizeof(PyObject *)
pourquoi supplémentaires 8
octets est supprimé à partir d' tp_basicsize
est quelque chose que je n'ai pas été en mesure de trouver. (Voir MSeifert commentaire pour une explication possible)
Mais, c'est en gros la différence dans votre exemple. list
s aussi garder autour d'un certain nombre de allouées éléments qui aide à déterminer quand à la sur-allouer de nouveau.
Maintenant, lorsque de nouveaux éléments sont ajoutés, les listes ne sont en effet effectuer cette sur-allocation pour atteindre O(1) ajoute. Il en résulte en grande taille comme MSeifert du couvre parfaitement dans sa réponse.