raspberry proyectos persona lector huella guardar control con biometrico python list memory-management python-internals

python - proyectos - guardar huella digital en access



¿Por qué dos listas idénticas tienen una huella de memoria diferente? (3)

l1 dos listas l1 y l2 , pero cada una con un método de creación diferente:

import sys l1 = [None] * 10 l2 = [None for _ in range(10)] print(''Size of l1 ='', sys.getsizeof(l1)) print(''Size of l2 ='', sys.getsizeof(l2))

Pero la salida me sorprendió:

Size of l1 = 144 Size of l2 = 192

La lista creada con una lista de comprensión tiene un tamaño mayor en la memoria, pero las dos listas son idénticas en Python de lo contrario.

¿Porqué es eso? ¿Es esto algo interno de CPython, o alguna otra explicación?


Como se señaló en esta pregunta, la lista-comprensión usa list.append bajo el capó, por lo que llamará el método de lista-cambio de tamaño, que generalocede.

Para demostrar esto a ti mismo, puedes usar el desensamblador:

>>> code = compile(''[x for x in iterable]'', '''', ''eval'') >>> import dis >>> dis.dis(code) 1 0 LOAD_CONST 0 (<code object <listcomp> at 0x10560b810, file "", line 1>) 2 LOAD_CONST 1 (''<listcomp>'') 4 MAKE_FUNCTION 0 6 LOAD_NAME 0 (iterable) 8 GET_ITER 10 CALL_FUNCTION 1 12 RETURN_VALUE Disassembly of <code object <listcomp> at 0x10560b810, file "", line 1>: 1 0 BUILD_LIST 0 2 LOAD_FAST 0 (.0) >> 4 FOR_ITER 8 (to 14) 6 STORE_FAST 1 (x) 8 LOAD_FAST 1 (x) 10 LIST_APPEND 2 12 JUMP_ABSOLUTE 4 >> 14 RETURN_VALUE >>>

Observe el código de operación LIST_APPEND en el desensamblaje del objeto de código <listcomp> . De los docs :

LIST_APPEND (i)

Llamadas list.append(TOS[-i], TOS) . Se utiliza para implementar listas de comprensión.

Ahora, para la operación de lista-repetición, tenemos una pista sobre lo que está pasando si consideramos:

>>> import sys >>> sys.getsizeof([]) 64 >>> 8*10 80 >>> 64 + 80 144 >>> sys.getsizeof([None]*10) 144

Por lo tanto, parece ser capaz de asignar exactamente el tamaño. Mirando el código fuente , vemos que esto es exactamente lo que sucede:

static PyObject * list_repeat(PyListObject *a, Py_ssize_t n) { Py_ssize_t i, j; Py_ssize_t size; PyListObject *np; PyObject **p, **items; PyObject *elem; if (n < 0) n = 0; if (n > 0 && Py_SIZE(a) > PY_SSIZE_T_MAX / n) return PyErr_NoMemory(); size = Py_SIZE(a) * n; if (size == 0) return PyList_New(0); np = (PyListObject *) PyList_New(size);

A saber, aquí: size = Py_SIZE(a) * n; . El resto de las funciones simplemente llena la matriz.


Cuando escribe [None] * 10 , Python sabe que necesitará una lista de exactamente 10 objetos, por lo que asigna exactamente eso.

Cuando usas una lista de comprensión, Python no sabe cuánto necesitará. Por lo que gradualmente crece la lista a medida que se agregan elementos. Para cada reasignación, asigna más espacio del que se necesita de inmediato, de modo que no tiene que reasignarse para cada elemento. Es probable que la lista resultante sea algo más grande de lo necesario.

Puede ver este comportamiento al comparar listas creadas con tamaños similares:

>>> sys.getsizeof([None]*15) 184 >>> sys.getsizeof([None]*16) 192 >>> sys.getsizeof([None for _ in range(15)]) 192 >>> sys.getsizeof([None for _ in range(16)]) 192 >>> sys.getsizeof([None for _ in range(17)]) 264

Puede ver que el primer método asigna solo lo que se necesita, mientras que el segundo crece periódicamente. En este ejemplo, asigna lo suficiente para 16 elementos, y tuvo que reasignarse al llegar al 17º.


Ninguno es un bloque de memoria, pero no es un tamaño preespecificado. Además de eso, hay un espacio adicional en una matriz entre los elementos de la matriz. Puedes ver esto tú mismo ejecutando:

for ele in l2: print(sys.getsizeof(ele)) >>>>16 16 16 16 16 16 16 16 16 16

Lo que no totaliza el tamaño de l2, sino que es menor.

print(sys.getsizeof([None])) 72

Y esto es mucho mayor que una décima parte del tamaño de l1 .

Sus números deben variar dependiendo de los detalles de su sistema operativo y los detalles del uso de memoria actual en su sistema operativo. El tamaño de [Ninguno] nunca puede ser más grande que la memoria adyacente disponible donde la variable está configurada para ser almacenada, y la variable puede tener que ser movida si posteriormente se asigna dinámicamente para ser más grande.