A tupleocupa menos espacio de memoria en Python:
>>> a = (1,2,3)
>>> a.__sizeof__()
48mientras que lists ocupa más espacio en la memoria:
>>> b = [1,2,3]
>>> b.__sizeof__()
64¿Qué sucede internamente en la gestión de memoria de Python?
A tupleocupa menos espacio de memoria en Python:
>>> a = (1,2,3)
>>> a.__sizeof__()
48mientras que lists ocupa más espacio en la memoria:
>>> b = [1,2,3]
>>> b.__sizeof__()
64¿Qué sucede internamente en la gestión de memoria de Python?
Respuestas:
Supongo que está usando CPython y con 64 bits (obtuve los mismos resultados en mi CPython 2.7 de 64 bits). Puede haber diferencias en otras implementaciones de Python o si tiene un Python de 32 bits.
Independientemente de la implementación,
listlos mensajes de correo electrónico son de tamaño variable mientras quetuples son de tamaño fijo.Entonces
tuples pueden almacenar los elementos directamente dentro de la estructura, las listas, por otro lado, necesitan una capa de indirección (almacena un puntero a los elementos). Esta capa de indirección es un puntero, en sistemas de 64 bits que son 64 bits, por lo tanto, 8 bytes.Pero hay otra cosa que
listsí: sobreasignan. Delist.appendlo contrario , sería unaO(n)operación siempre : para que se amorticeO(1)(¡mucho más rápido!), Se sobreasigna. Pero ahora tiene que realizar un seguimiento del tamaño asignado y el tamaño de relleno (tuplesolo es necesario almacenar un tamaño, porque el tamaño asignado y el tamaño de relleno son siempre idénticos). Eso significa que cada lista tiene que almacenar otro "tamaño" que en los sistemas de 64 bits es un número entero de 64 bits, de nuevo 8 bytes.Entonces, los
lists necesitan al menos 16 bytes más de memoria que lostuples. ¿Por qué dije "al menos"? Debido a la sobreasignación. La sobreasignación significa que asigna más espacio del necesario. Sin embargo, la cantidad de sobreasignación depende de "cómo" crea la lista y el historial de anexos / eliminaciones:Imagenes
Decidí crear algunas imágenes para acompañar la explicación anterior. Quizás estos sean útiles
Así es como (esquemáticamente) se almacena en la memoria en su ejemplo. Destaqué las diferencias con los ciclos rojos (a mano alzada):
En realidad, eso es solo una aproximación porque los
intobjetos también son objetos de Python y CPython incluso reutiliza números enteros pequeños, por lo que una representación probablemente más precisa (aunque no tan legible) de los objetos en la memoria sería:Enlaces útiles:
tupleestructura en el repositorio CPython para Python 2.7listestructura en el repositorio CPython para Python 2.7intestructura en el repositorio CPython para Python 2.7¡Tenga en cuenta que
__sizeof__realmente no devuelve el tamaño "correcto"! Solo devuelve el tamaño de los valores almacenados. Sin embargo, cuando usasys.getsizeofel resultado es diferente:Hay 24 bytes "extra". Estos son reales , esa es la sobrecarga del recolector de basura que no se tiene en cuenta en el
__sizeof__método. Esto se debe a que generalmente no se supone que use métodos mágicos directamente; use las funciones que saben cómo manejarlos, en este caso:sys.getsizeof(que en realidad agrega la sobrecarga de GC al valor devuelto__sizeof__).fuente
listla asignación de memoria stackoverflow.com/questions/40018398/…list()o una lista de comprensión.Profundizaré en el código base de CPython para que podamos ver cómo se calculan realmente los tamaños. En su ejemplo específico , no se han realizado sobreasignaciones, así que no tocaré eso .
Voy a usar valores de 64 bits aquí, como tú.
El tamaño de
lists se calcula a partir de la siguiente función,list_sizeof:Aquí
Py_TYPE(self)hay una macro que toma elob_typedeself(regresandoPyList_Type) mientras que_PyObject_SIZEes otra macro que tomatp_basicsizede ese tipo.tp_basicsizese calcula comosizeof(PyListObject)dóndePyListObjectestá la estructura de la instancia.La
PyListObjectestructura tiene tres campos:estos tienen comentarios (que recorté) que explican qué son, siga el enlace de arriba para leerlos.
PyObject_VAR_HEADse expande en tres campos de 8 bytes (ob_refcount,ob_typeyob_size) por lo que es una24contribución de bytes.Entonces por ahora
reses:o:
Si la instancia de la lista tiene elementos asignados. la segunda parte calcula su contribución.
self->allocated, como su nombre lo indica, contiene el número de elementos asignados.Sin ningún elemento, el tamaño de las listas se calcula como:
es decir, el tamaño de la estructura de la instancia.
tuplelos objetos no definen unatuple_sizeoffunción. En cambio, usanobject_sizeofpara calcular su tamaño:Esto, en cuanto a
lists, toma eltp_basicsizey, si el objeto tiene un valor distinto de cerotp_itemsize(lo que significa que tiene instancias de longitud variable), multiplica el número de elementos en la tupla (que obtiene a través dePy_SIZE) contp_itemsize.tp_basicsizenuevamente usasizeof(PyTupleObject)donde laPyTupleObjectestructura contiene :Entonces, sin ningún elemento (es decir,
Py_SIZEdevoluciones0), el tamaño de las tuplas vacías es igual asizeof(PyTupleObject):eh Bueno, aquí hay una rareza para la que no he encontrado una explicación, la
tp_basicsizedetuples se calcula de la siguiente manera:por qué
8se eliminan bytes adicionalestp_basicsizees algo que no he podido averiguar. (Ver el comentario de MSeifert para una posible explicación)Pero, esta es básicamente la diferencia en su ejemplo específico .
lists también mantienen una serie de elementos asignados que ayuda a determinar cuándo sobreasignar nuevamente.Ahora, cuando se agregan elementos adicionales, las listas sí realizan esta sobreasignación para lograr agregados O (1). Esto da como resultado tamaños más grandes, ya que MSeifert cubre muy bien en su respuesta.
fuente
ob_item[1]es principalmente un marcador de posición (por lo que tiene sentido que se reste del tamaño básico). Eltuplese asigna usandoPyObject_NewVar. No he descubierto los detalles, así que es solo una suposiciónLa respuesta de MSeifert lo cubre ampliamente; para hacerlo simple, puede pensar en:
tuplees inmutable. Una vez configurado, no puede cambiarlo. Entonces, sabe de antemano cuánta memoria necesita asignar para ese objeto.listes mutable. Puede agregar o quitar elementos de él. Tiene que saber su tamaño (para impl. Interna). Cambia de tamaño según sea necesario.No hay comidas gratis ; estas capacidades tienen un costo. De ahí la sobrecarga en la memoria para las listas.
fuente
El tamaño de la tupla tiene un prefijo, lo que significa que en la inicialización de la tupla el intérprete asigna suficiente espacio para los datos contenidos, y ese es el final, lo que le da inmutable (no se puede modificar), mientras que una lista es un objeto mutable, por lo tanto, implica dinámica asignación de memoria, por lo que para evitar asignar espacio cada vez que agrega o modifica la lista (asigne suficiente espacio para contener los datos cambiados y copie los datos), asigna espacio adicional para futuras adiciones, modificaciones, ... eso prácticamente lo resume.
fuente