str - Comprender la asignación de memoria para enteros grandes en Python
str en python (2)
¿Cómo asigna Python la memoria para enteros grandes?
Un tipo int
tiene un tamaño de 28 bytes
y a medida que sigo aumentando el valor de int
, el tamaño aumenta en incrementos de 4 bytes
.
¿Por qué
28 bytes
inicialmente para cualquier valor tan bajo como1
?¿Por qué incrementos de
4 bytes
?
PD: estoy ejecutando Python 3.5.2 en una x86_64 (máquina de 64 bits). Cualquier puntero / recurso / PEP sobre cómo los intérpretes (3.0+) trabajan en números tan grandes es lo que estoy buscando.
Código que ilustra los tamaños:
>>> a=1
>>> print(a.__sizeof__())
28
>>> a=1024
>>> print(a.__sizeof__())
28
>>> a=1024*1024*1024
>>> print(a.__sizeof__())
32
>>> a=1024*1024*1024*1024
>>> print(a.__sizeof__())
32
>>> a=1024*1024*1024*1024*1024*1024
>>> a
1152921504606846976
>>> print(a.__sizeof__())
36
¿Por qué
28
bytes inicialmente para cualquier valor tan bajo como1
?
Creo que @bgusach respondió eso completamente; Python usa estructuras C
para representar objetos en el mundo de Python, cualquier objeto incluyendo int
s :
struct _longobject {
PyObject_VAR_HEAD
digit ob_digit[1];
};
PyObject_VAR_HEAD
es una macro que cuando se expande agrega otro campo en la estructura (campo PyVarObject
que se usa específicamente para objetos que tienen alguna noción de longitud) y, ob_digits
es una matriz que contiene el valor para el número. El tamaño de la placa de caldera proviene de esa estructura, para números pequeños y grandes de Python.
¿Por qué incrementos de
4
bytes?
Porque, cuando se crea un número mayor, el tamaño (en bytes) es un múltiplo del tamaño de sizeof(digit)
; Puedes ver eso en _PyLong_New
donde la asignación de memoria para un nuevo objeto longobject
se realiza con PyObject_MALLOC
:
/* Number of bytes needed is: offsetof(PyLongObject, ob_digit) +
sizeof(digit)*size. Previous incarnations of this code used
sizeof(PyVarObject) instead of the offsetof, but this risks being
incorrect in the presence of padding between the PyVarObject header
and the digits. */
if (size > (Py_ssize_t)MAX_LONG_DIGITS) {
PyErr_SetString(PyExc_OverflowError,
"too many digits in integer");
return NULL;
}
result = PyObject_MALLOC(offsetof(PyLongObject, ob_digit) +
size*sizeof(digit));
offsetof(PyLongObject, ob_digit)
es la ''placa de la caldera'' (en bytes) para el objeto largo que no está relacionado con la retención de su valor.
digit
se define en el archivo de encabezado que contiene struct _longobject
como typedef
para uint32
:
typedef uint32_t digit;
y sizeof(uint32_t)
tiene 4
bytes. Esa es la cantidad por la que verá que el tamaño en bytes aumenta cuando aumenta el argumento de size
a _PyLong_New
.
Por supuesto, así es como C
Python ha elegido implementarlo. Es un detalle de implementación y, como tal, no encontrará mucha información en PEP. La lista de correo de python-dev mantendría discusiones de implementación si puede encontrar el hilo correspondiente :-).
De cualquier manera, es posible que encuentre un comportamiento diferente en otras implementaciones populares, por lo que no lo tome por hecho.
En realidad es fácil. Python''s int
no es el tipo de primitivo al que puedes estar acostumbrado en otros idiomas, sino un objeto completo, con sus métodos y todo lo demás. De ahí viene la sobrecarga.
Entonces, usted tiene la carga útil en sí misma, el número entero que se está representando. Y no hay límite para eso, excepto tu memoria.
El tamaño del int
de Python es lo que necesita para representar el número más un poco de sobrecarga.
Si desea leer más a fondo, eche un vistazo a la parte relevante de la documentación :
Los enteros tienen una precisión ilimitada