web-dev-qa-db-de.com

Unterschiedliche Objektgröße von True und False in Python 3

Experimentieren mit magischen Methoden (__sizeof__ insbesondere) auf verschiedenen Python Objekten bin ich auf folgendes Verhalten gestoßen:

Python 2.7

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

Python 3.x

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

Was hat sich in Python 3 geändert, das die Größe von True größer macht als die Größe von False?

67
Simon Fromme

Dies liegt daran, dass bool eine Unterklasse von int in Python 2 und 3 ist.

>>> issubclass(bool, int)
True

Aber die Implementierung von int hat sich geändert.

In Python 2 war int derjenige, der 32 oder 64 Bit betrug, abhängig vom System, im Gegensatz zu long mit willkürlicher Länge.

In Python 3 ist int beliebig lang - der long von Python 2 wurde in int und der ursprüngliche Python umbenannt 2 int insgesamt fallen gelassen.


In Python 2 erhalten Sie dasselbe Verhalten für long Objekte 1L Und 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

Das long/Python 3 int ist ein Objekt mit variabler Länge, genau wie ein Tupel. Wenn es zugewiesen wird, wird genügend Speicher zugewiesen, um alle für seine Darstellung erforderlichen Binärziffern aufzunehmen. Die Länge des variablen Teils wird im Objektkopf gespeichert. 0 Erfordert keine Binärziffern (die variable Länge ist 0), aber auch 1 Geht über und erfordert zusätzliche Ziffern.

Das heißt 0 Wird als binärer String der Länge 0 dargestellt:

<>

und 1 wird als 30-Bit-Binärzeichenfolge dargestellt:

<000000000000000000000000000001>

Die Standardkonfiguration in Python verwendet Bits in einem uint32_t ; so 2**30 - 1 Passt immer noch in 28 Bytes auf x86-64 und 2**30 Benötigt 32;

2**30 - 1 Wird dargestellt als

<111111111111111111111111111111>

alle 30 Wertebits auf 1 gesetzt; 2 ** 30 wird mehr brauchen, und es wird interne Vertretung haben

<000000000000000000000000000001000000000000000000000000000000>

Wie für True mit 28 Bytes anstelle von 24 - Sie brauchen sich keine Sorgen zu machen. True ist ein Singleton und daher gehen nur 4 Bytes in total in Python Programm, nicht 4 für jede Verwendung von True.

61
Antti Haapala

Sowohl True als auch False sind longobjects in CPython:

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

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

Man kann also sagen, dass ein Boolescher Wert eine Unterklasse von python-3.xint ist, wobei True den Wert 1 Annimmt, und False nimmt als Wert 0 an. Wir rufen also PyVarObject_HEAD_INIT mit als type -Parameter auf PyBool_Type Und mit ob_size Als Wert 0 Bzw. 1.

Seit python-3.x gibt es kein long mehr: Diese wurden zusammengeführt, und das int -Objekt wird abhängig von der Größe des Zahl, nehmen Sie einen anderen Wert.

Wenn wir den Quellcode des Typs longlobject untersuchen, sehen wir:

/* 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];
};

Um es kurz zu machen, ein _longobject Kann als ein Array von "Ziffern" gesehen werden, aber Sie sollten hier Ziffern nicht als Dezimalziffern sehen, sondern als Gruppen von Bits, die auf diese Weise addiert, multipliziert usw. Werden können .

Nun, wie im Kommentar angegeben, heißt es:

   zero is represented by ob_size == 0.

Für den Fall, dass der Wert Null ist, werden keine Ziffern hinzugefügt, während für kleine ganze Zahlen (Werte kleiner als 2)30 in CPython) dauert es eine Ziffer und so weiter.

In python-2.x gab es zwei Arten von Darstellungen für Zahlen, ints (mit einer festen Größe), Sie konnten dies als "eine Ziffer" und longs mit mehreren Ziffern. Da eine bool eine Unterklasse von int war, nahmen sowohl True als auch False den gleichen Platz ein.

19

Schauen Sie sich den cpython code für True und False an

Intern wird es als Ganzzahl dargestellt

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

Ich habe keinen CPython-Code dafür gesehen, aber ich glaube, das hat etwas mit der Optimierung von Ganzzahlen in Python 3 zu tun. Wahrscheinlich wurden einige Optimierungen vereinheitlicht, als long gelöscht wurde. int in Python 3 hat eine beliebige Größe int - das gleiche wie long in Python 2. Da bool auf die gleiche Weise wie neu int gespeichert wird, es betrifft beides.

Interessanter Teil:

>>> (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

+ Bytes für Objektüberschriften sollten die Gleichung vervollständigen.

6
Slam