76 votes

Taille différente des objets de True et False en Python 3

Expérimenter des méthodes magiques ( __sizeof__ en particulier) sur différents objets 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 et qui fait que la taille de l'objet de l'opération est plus grande que celle de l'objet de l'opération ? True supérieure à la taille de False ?

3 votes

Related le même comportement apparaît pour 0 vs 1

0 votes

Notez que l'évaluation de la consommation de mémoire à l'aide de sys.getsizeof y __sizeof__ (le dernier manque l'overhead GC) conduira à des résultats trompeurs à moins de vraiment, vraiment comprendre l'interpréteur Python. PyPy le considère comme un erreur pour utiliser l'un d'entre eux. Par exemple, les entiers 5 <= i <= 256 sont des singletons dans CPython - [1, 1] y [1, 1, 1, 1] ne diffèrent que par deux pointeurs supplémentaires en taille. Dans votre cas, vous devez déterminer si True y 1 partagent la même mémoire pour leur valeur.

71voto

Antti Haapala Points 11542

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

>>> issubclass(bool, int)
True

Mais le int a changé.

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

Dans Python 3, int est de longueur arbitraire - le long de Python 2 a été renommé en int et la version originale de Python 2 int a été abandonné.


En Python 2, vous obtenez exactement le même comportement pour long objets 1L y 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

El 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 pour le représenter. La longueur de la partie variable est stockée dans l'en-tête de l'objet. 0 ne requiert aucun chiffre binaire (sa longueur variable est de 0), mais même 1 déborde, et nécessite des chiffres supplémentaires.

I.e. 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 dans Python utilise 30 bits dans un uint32_t ; so 2**30 - 1 tient toujours dans 28 octets sur x86-64, et 2**30 nécessitera 32 ;

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

<111111111111111111111111111111>

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

<000000000000000000000000000001000000000000000000000000000000>

Quant à True en utilisant 28 octets au lieu de 24 - vous ne devez pas vous inquiéter. True est un singleton et donc seulement 4 octets sont perdus dans total dans tout programme Python, et non 4 pour chaque utilisation de True .

22voto

CommuSoft Points 6439

Les deux sites True y False sont longobject s dans CPython :

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

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

On peut donc dire qu'un booléen est une sous-classe d'un python-3.x intTrue prend comme valeur 1 y False prend comme valeur 0 . Nous faisons donc un appel à PyVarObject_HEAD_INIT avec comme type une référence à PyBool_Type et avec ob_size comme valeur 0 y 1 respectivement.

Maintenant que python-3.x il n'y a pas de long plus : ils ont été fusionnés, et le int prendra, en fonction de la taille du nombre, une valeur différente.

Si nous inspectons le code source de la 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 longue histoire courte, un _longobject peut être vu comme un tableau de "chiffres", mais il faut ici voir les chiffres non pas comme des chiffres décimaux, mais comme des groupes de bits qui peuvent donc être additionnés, multipliés, etc.

Maintenant, comme il est précisé dans le commentaire, il est dit que :

   zero is represented by ob_size == 0.

Donc, dans le cas où la valeur est zéro, no chiffres sont ajoutés, alors que pour les petits entiers (valeurs inférieures à 2 30 en CPython), il prend un chiffre, et ainsi de suite.

En python-2.x il y avait deux types de représentations pour les nombres, int (avec une taille fixe), vous pourriez voir cela comme "un chiffre", et long s, avec plusieurs chiffres. Puisqu'un bool était une sous-classe de int les deux True y False occupaient le même espace.

6voto

Slam Points 3117

Je n'ai pas vu le code CPython pour cela, mais je crois que cela a quelque chose à voir avec l'optimisation des entiers dans Python 3. Probablement, comme long a été abandonné, certaines optimisations ont été unifiées. int dans Python 3 est un int de taille arbitraire - le même que long était dans Python 2. Comme bool de la même manière que les nouvelles int il 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'objets devraient compléter l'équation.

5voto

Kamil Niski Points 3164

Jetez un coup d'œil à la code cpython para True y False

En interne, il est représenté par un nombre 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 }

0 votes

Je pense que je n'ai pas été aussi rapide que les autres pour répondre correctement :)

2 votes

La réponse ne fait que la moitié du chemin. C'est représenté comme un nombre entier ouais ? Et alors quoi ?

0 votes

@wim Je ne vois pas l'utilité d'expliquer davantage puisque les personnes ci-dessus ont déjà fait un excellent travail à ce sujet. Je ne veux pas dupliquer le contenu. Veuillez vous référer à leurs réponses.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X