python python-3.x python-2.7 cpython python-internals

Tamaño de objeto diferente de Verdadero y Falso en Python 3



python-3.x python-2.7 (4)

Eche un vistazo al código cpython para True y False

Internamente se representa como un entero

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 }

Experimentando con métodos mágicos ( __sizeof__ en particular) en diferentes objetos de Python me topé con el siguiente comportamiento:

Python 2.7

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

Python 3.x

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

¿Qué cambió en Python 3 que hace que el tamaño de True mayor que el tamaño de False ?


No he visto el código CPython para esto, pero creo que esto tiene algo que ver con la optimización de los enteros en Python 3. Probablemente, mientras se eliminó, algunas optimizaciones se unificaron. int en Python 3 es int de tamaño arbitrario, el mismo long estuvo en Python 2. Como bool almacena de la misma manera que new int , afecta a ambos.

Parte interesante:

>>> (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 para encabezados de objetos deben completar la ecuación.


Se debe a que bool es una subclase de int en Python 2 y 3.

>>> issubclass(bool, int) True

Pero la implementación de int ha cambiado.

En Python 2, int era el que tenía 32 o 64 bits, dependiendo del sistema, en contraposición a la longitud arbitraria.

En Python 3, int es de longitud arbitraria: la parte long de Python 2 se cambió de nombre a int y la int original de Python 2 se eliminó por completo.

En Python 2, obtienes exactamente el mismo comportamiento para los objetos largos 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

long / Python 3 int es un objeto de longitud variable, como una tupla: cuando se asigna, se asigna suficiente memoria para contener todos los dígitos binarios necesarios para representarlo. La longitud de la parte variable se almacena en la cabeza del objeto. 0 no requiere dígitos binarios (su longitud variable es 0), pero incluso 1 derrama, y ​​requiere dígitos adicionales.

Ie 0 se representa como una cadena binaria de longitud 0:

<>

y 1 se representa como una cadena binaria de 30 bits:

<000000000000000000000000000001>

La configuración predeterminada en Python usa 30 bits en un uint32_t ; so 2**30 - 1 aún encaja en 28 bytes en x86-64, y 2**30 requerirá 32;

2**30 - 1 será presentado como

<111111111111111111111111111111>

es decir, todos los 30 bits de valor establecidos en 1; 2 ** 30 necesitarán más, y tendrá representación interna.

<000000000000000000000000000001000000000000000000000000000000>

En cuanto a True utiliza 28 bytes en lugar de 24 ; no debes preocuparte. True es un singleton y, por lo tanto, solo se pierden 4 bytes en total en cualquier programa de Python, no 4 por cada uso de True .


Tanto el True como el False son longobject en CPython:

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

Por lo tanto, puede decir que un valor booleano es una subclase de un int python-3.x donde True toma como valor 1 , y False toma como valor 0 . Por lo tanto, hacemos una llamada a PyVarObject_HEAD_INIT con como parámetro de type una referencia a PyBool_Type y con ob_size como valor 0 y 1 respectivamente.

Ahora, desde python-3.x , ya no hay más: se han fusionado, y el objeto int , dependiendo del tamaño del número, tomará un valor diferente.

Si inspeccionamos el código fuente del tipo longlobject , vemos:

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

Para _longobject , un objeto _longobject puede verse como una matriz de "dígitos", pero aquí debería ver los dígitos no como dígitos decimales, sino como grupos de bits que, por lo tanto, se pueden agregar, multiplicar, etc.

Ahora como se especifica en el comentario, dice que:

zero is represented by ob_size == 0.

Entonces, en caso de que el valor sea cero, no se agregan dígitos, mientras que para los enteros pequeños (valores inferiores a 2 30 en CPython), toma un dígito, y así sucesivamente.

En python-2.x , había dos tipos de representaciones para números, int s (con un tamaño fijo), se podía ver como "un dígito" y long s, con varios dígitos. Dado que un bool era una subclase de int , True y False ocupaban el mismo espacio.