web-dev-qa-db-fra.com

Taille d'objet différente de True et False dans Python 3

Expérimenter avec des méthodes magiques (__sizeof__ _ en particulier) sur différents Python je suis tombé sur le comportement suivant:

Python 2.7

>>> False.__sizeof__()
24
>>> True.__sizeof__()
24

Python 3.x

>>> False.__sizeof__()
24
>>> True.__sizeof__()
28

Qu'est-ce qui a changé dans Python 3 qui rend la taille de True supérieure à celle de False?

67
Simon Fromme

C'est parce que bool est une sous-classe de int dans les deux Python 2 et 3.

>>> issubclass(bool, int)
True

Mais l'implémentation int a changé.

Dans Python 2, int était celui qui faisait 32 ou 64 bits, selon le système, par opposition à longueur arbitraire long.

Dans Python 3, int est une longueur arbitraire - le long de Python 2 a été renommé en int et l'original Python 2 int totalement abandonné.


En Python 2 vous obtenez exactement le même comportement pour long objets 1L et 0L:

Python 2.7.15rc1 (default, Apr 15 2018, 21:51:34) 
[GCC 7.3.0] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import sys
>>> sys.getsizeof(1L)
28
>>> sys.getsizeof(0L)
24

Le long/Python 3 int est un objet de longueur variable, tout comme un tuple - lorsqu'il est alloué, suffisamment de mémoire est allouée pour contenir tous les chiffres binaires nécessaires à sa représentation. La longueur de la partie variable est stockée dans la tête de l'objet. 0 ne nécessite pas de chiffres binaires (sa longueur variable est 0), mais même 1 déborde et nécessite des chiffres supplémentaires.

C'est à dire. 0 est représenté par une chaîne binaire de longueur 0:

<>

et 1 est représenté par une chaîne binaire de 30 bits:

<000000000000000000000000000001>

La configuration par défaut de Python utilise bits dans un uint32_t ; so 2**30 - 1 tient toujours dans 28 octets sur x86-64, et 2**30 il en faudra 32;

2**30 - 1 sera présenté comme

<111111111111111111111111111111>

c'est-à-dire tous les 30 bits de valeur mis à 1; 2 ** 30 auront besoin de plus, et il aura une représentation interne

<000000000000000000000000000001000000000000000000000000000000>

Comme pour True utiliser 28 octets au lieu de 24 - ne vous inquiétez pas. True est un singleton et, par conséquent, seulement 4 octets sont perdus dans total dans tout Python, pas 4 pour chaque utilisation de True.

61
Antti Haapala

True et False sont tous deux longobjects en CPython:

struct _longobject _Py_FalseStruct = {
    PyVarObject_HEAD_INIT(&PyBool_Type, 0)
    { 0 }
};

struct _longobject _Py_TrueStruct = {
    PyVarObject_HEAD_INIT(&PyBool_Type, 1)
    { 1 }
};

Vous pouvez donc dire qu'un booléen est une sous-classe d'un python-3.xintTrue prend pour valeur 1 et False prend pour valeur 0. Nous faisons donc un appel à PyVarObject_HEAD_INIT avec comme paramètre type une référence à PyBool_Type et avec ob_size comme valeur 0 et 1 respectivement.

Maintenant depuis python-3.x , il n'y a plus de long: ils ont été fusionnés et l'objet int sera, en fonction de la taille du nombre, prendre une valeur différente.

Si nous inspectons le code source du type longlobject type , nous voyons:

/* Long integer representation.
   The absolute value of a number is equal to
        SUM(for i=0 through abs(ob_size)-1) ob_digit[i] * 2**(SHIFT*i)
   Negative numbers are represented with ob_size < 0;
   zero is represented by ob_size == 0.
   In a normalized number, ob_digit[abs(ob_size)-1] (the most significant
   digit) is never zero. Also, in all cases, for all valid i,
        0 <= ob_digit[i] <= MASK.
   The allocation function takes care of allocating extra memory
   so that ob_digit[0] ... ob_digit[abs(ob_size)-1] are actually available.
   CAUTION: Generic code manipulating subtypes of PyVarObject has to
   aware that ints abuse ob_size's sign bit.
*/

struct _longobject {
    PyObject_VAR_HEAD
    digit ob_digit[1];
};

Pour faire une histoire courte, un _longobject peut être vu comme un tableau de "chiffres", mais vous devriez voir ici les chiffres non pas comme des chiffres décimaux, mais comme des groupes de bits qui peuvent ainsi être ajoutés, multipliés, etc.

Maintenant, comme spécifié dans le commentaire, il est dit que:

   zero is represented by ob_size == 0.

Ainsi, dans le cas où la valeur est zéro, aucun chiffre n'est ajouté, alors que pour les petits entiers (valeurs inférieures à 230 dans CPython), il faut un chiffre, et ainsi de suite.

Dans python-2.x , il y avait deux types de représentations pour les nombres, ints (avec une taille fixe), on pouvait voir cela comme "un chiffre" et longs, avec plusieurs chiffres. Étant donné que bool était une sous-classe de int, True et False occupaient le même espace.

19
Willem Van Onsem

Jetez un coup d'œil au code cpython pour True et False

En interne, il est représenté sous forme d'entier

PyTypeObject PyBool_Type = {
        PyVarObject_HEAD_INIT(&PyType_Type, 0)
        "bool",
        sizeof(struct _longobject),
        0,
        0,                                          /* tp_dealloc */
        0,                                          /* tp_print */
        0,                                          /* tp_getattr */
        0,                                          /* tp_setattr */
        0,                                          /* tp_reserved */
        bool_repr,                                  /* tp_repr */
        &bool_as_number,                            /* tp_as_number */
        0,                                          /* tp_as_sequence */
        0,                                          /* tp_as_mapping */
        0,                                          /* tp_hash */
        0,                                          /* tp_call */
        bool_repr,                                  /* tp_str */
        0,                                          /* tp_getattro */
        0,                                          /* tp_setattro */
        0,                                          /* tp_as_buffer */
        Py_TPFLAGS_DEFAULT,                         /* tp_flags */
        bool_doc,                                   /* tp_doc */
        0,                                          /* tp_traverse */
        0,                                          /* tp_clear */
        0,                                          /* tp_richcompare */
        0,                                          /* tp_weaklistoffset */
        0,                                          /* tp_iter */
        0,                                          /* tp_iternext */
        0,                                          /* tp_methods */
        0,                                          /* tp_members */
        0,                                          /* tp_getset */
        &PyLong_Type,                               /* tp_base */
        0,                                          /* tp_dict */
        0,                                          /* tp_descr_get */
        0,                                          /* tp_descr_set */
        0,                                          /* tp_dictoffset */
        0,                                          /* tp_init */
        0,                                          /* tp_alloc */
        bool_new,                                   /* tp_new */
    };

    /* The objects representing bool values False and True */

    struct _longobject _Py_FalseStruct = {
        PyVarObject_HEAD_INIT(&PyBool_Type, 0)
        { 0 }
    };

    struct _longobject _Py_TrueStruct = {
        PyVarObject_HEAD_INIT(&PyBool_Type, 1)
    { 1 }
6
Kamil Niski

Je n'ai pas vu le code CPython pour cela, mais je pense que cela a quelque chose à voir avec l'optimisation des entiers dans Python 3. Probablement, puisque long a été abandonné, certaines optimisations ont été unifiées. int en Python 3 est de taille arbitraire - identique à long en Python 2. As bool est stocké de la même manière que new int, cela affecte les deux.

Partie intéressante:

>>> (0).__sizeof__()
24

>>> (1).__sizeof__()  # Here one more "block" is allocated
28

>>> (2**30-1).__sizeof__()  # This is the maximum integer size fitting into 28
28

+ octets pour les en-têtes d'objet devraient compléter l'équation.

6
Slam