varios una serie operaciones numeros misma llenar listas lista length instruccion generar funcion eliminar elementos definir con comparar como python performance list tuples python-internals

una - operaciones con listas python



¿Son las tuplas más eficientes que las listas en Python? (6)

¿Hay alguna diferencia de rendimiento entre tuplas y listas cuando se trata de creación de instancias y recuperación de elementos?


Resumen

Las tuplas tienden a tener un mejor rendimiento que las listas en casi todas las categorías:

1) Las tuplas se pueden doblar constantemente .

2) Las tuplas pueden reutilizarse en lugar de copiarse.

3) Las tuplas son compactas y no se sobreasignan.

4) Las tuplas hacen referencia directa a sus elementos.

Las tuplas se pueden doblar constantemente

Las tuplas de constantes se pueden calcular previamente mediante el optimizador de mirilla de Python o el optimizador de AST. Las listas, por otro lado, se construyen desde cero:

>>> from dis import dis >>> dis(compile("(10, ''abc'')", '''', ''eval'')) 1 0 LOAD_CONST 2 ((10, ''abc'')) 3 RETURN_VALUE >>> dis(compile("[10, ''abc'']", '''', ''eval'')) 1 0 LOAD_CONST 0 (10) 3 LOAD_CONST 1 (''abc'') 6 BUILD_LIST 2 9 RETURN_VALUE

Las tuplas no necesitan ser copiadas

Al ejecutar tuple(some_tuple) vuelve inmediatamente a sí mismo. Como las tuplas son inmutables, no es necesario copiarlas:

>>> a = (10, 20, 30) >>> b = tuple(a) >>> a is b True

Por el contrario, list(some_list) requiere que todos los datos sean copiados a una nueva lista:

>>> a = [10, 20, 30] >>> b = list(a) >>> a is b False

Tuplas no sobre asigna

Dado que el tamaño de una tupla es fijo, puede almacenarse de forma más compacta que las listas que necesitan sobreasignar para hacer que las operaciones de adición sean eficientes.

Esto le da a las tuplas una buena ventaja de espacio:

>>> import sys >>> sys.getsizeof(tuple(iter(range(10)))) 128 >>> sys.getsizeof(list(iter(range(10)))) 200

Aquí está el comentario de Objects / listobject.c que explica qué hacen las listas:

/* This over-allocates proportional to the list size, making room * for additional growth. The over-allocation is mild, but is * enough to give linear-time amortized behavior over a long * sequence of appends() in the presence of a poorly-performing * system realloc(). * The growth pattern is: 0, 4, 8, 16, 25, 35, 46, 58, 72, 88, ... * Note: new_allocated won''t overflow because the largest possible value * is PY_SSIZE_T_MAX * (9 / 8) + 6 which always fits in a size_t. */

Las tuplas se refieren directamente a sus elementos

Las referencias a los objetos se incorporan directamente en un objeto de tupla. Por el contrario, las listas tienen una capa adicional de direccionamiento indirecto a una matriz externa de punteros.

Esto le da a las tuplas una ventaja de velocidad pequeña para búsquedas indexadas y desempaquetado:

$ python3.6 -m timeit -s ''a = (10, 20, 30)'' ''a[1]'' 10000000 loops, best of 3: 0.0304 usec per loop $ python3.6 -m timeit -s ''a = [10, 20, 30]'' ''a[1]'' 10000000 loops, best of 3: 0.0309 usec per loop $ python3.6 -m timeit -s ''a = (10, 20, 30)'' ''x, y, z = a'' 10000000 loops, best of 3: 0.0249 usec per loop $ python3.6 -m timeit -s ''a = [10, 20, 30]'' ''x, y, z = a'' 10000000 loops, best of 3: 0.0251 usec per loop

Here muestra cómo se almacena la tupla (10, 20) :

typedef struct { Py_ssize_t ob_refcnt; struct _typeobject *ob_type; Py_ssize_t ob_size; PyObject *ob_item[2]; /* store a pointer to 10 and a pointer to 20 */ } PyTupleObject;

Así es como se almacena la lista [10, 20] :

PyObject arr[2]; /* store a pointer to 10 and a pointer to 20 */ typedef struct { Py_ssize_t ob_refcnt; struct _typeobject *ob_type; Py_ssize_t ob_size; PyObject **ob_item = arr; /* store a pointer to the two-pointer array */ Py_ssize_t allocated; } PyListObject;

Tenga en cuenta que el objeto tupla incorpora los dos punteros de datos directamente mientras que el objeto de la lista tiene una capa adicional de direccionamiento indirecto a una matriz externa que contiene los dos punteros de datos.


En general, puede esperar que las tuplas sean ligeramente más rápidas. Sin embargo, definitivamente debe probar su caso específico (si la diferencia puede afectar el rendimiento de su programa, recuerde que "la optimización prematura es la raíz de todo mal").

Python lo hace muy fácil: timeit es tu amigo.

$ python -m timeit "x=(1,2,3,4,5,6,7,8)" 10000000 loops, best of 3: 0.0388 usec per loop $ python -m timeit "x=[1,2,3,4,5,6,7,8]" 1000000 loops, best of 3: 0.363 usec per loop

y...

$ python -m timeit -s "x=(1,2,3,4,5,6,7,8)" "y=x[3]" 10000000 loops, best of 3: 0.0938 usec per loop $ python -m timeit -s "x=[1,2,3,4,5,6,7,8]" "y=x[3]" 10000000 loops, best of 3: 0.0649 usec per loop

Entonces, en este caso, la creación de instancias es casi un orden de magnitud más rápida para la tupla, ¡pero el acceso a los elementos es en realidad algo más rápido para la lista! Entonces, si está creando algunas tuplas y accediendo a ellas muchas veces, en realidad puede ser más rápido usar listas.

Por supuesto, si desea cambiar un elemento, la lista será más rápida, ya que necesitaría crear una tupla entera nueva para cambiar un elemento (ya que las tuplas son inmutables).


Las tuplas deberían ser un poco más eficientes y, por eso, más rápidas que las listas porque son inmutables.


Las tuplas, al ser inmutables, son más eficientes en la memoria; enumera, para mayor eficiencia, sobreajustar la memoria para permitir anexos sin realloc constantes. Entonces, si desea iterar a través de una secuencia constante de valores en su código (por ejemplo, for direction in ''up'', ''right'', ''down'', ''left'': :), se prefieren las tuplas, ya que dichas tuplas están precalculadas en tiempo de compilación.

Las velocidades de acceso deben ser las mismas (ambas se almacenan como matrices contiguas en la memoria).

Pero, alist.append(item) es mucho más preferido que atuple+= (item,) cuando manejas datos mutables. Recuerde, las tuplas están destinadas a ser tratadas como registros sin nombres de campo.


También debe considerar el módulo de array en la biblioteca estándar si todos los elementos de su lista o tupla son del mismo tipo. Puede ser más rápido y tomar menos memoria.


El módulo dis desmonta el código de bytes para una función y es útil para ver la diferencia entre tuplas y listas.

En este caso, puede ver que acceder a un elemento genera un código idéntico, pero que la asignación de una tupla es mucho más rápida que la asignación de una lista.

>>> def a(): ... x=[1,2,3,4,5] ... y=x[2] ... >>> def b(): ... x=(1,2,3,4,5) ... y=x[2] ... >>> import dis >>> dis.dis(a) 2 0 LOAD_CONST 1 (1) 3 LOAD_CONST 2 (2) 6 LOAD_CONST 3 (3) 9 LOAD_CONST 4 (4) 12 LOAD_CONST 5 (5) 15 BUILD_LIST 5 18 STORE_FAST 0 (x) 3 21 LOAD_FAST 0 (x) 24 LOAD_CONST 2 (2) 27 BINARY_SUBSCR 28 STORE_FAST 1 (y) 31 LOAD_CONST 0 (None) 34 RETURN_VALUE >>> dis.dis(b) 2 0 LOAD_CONST 6 ((1, 2, 3, 4, 5)) 3 STORE_FAST 0 (x) 3 6 LOAD_FAST 0 (x) 9 LOAD_CONST 2 (2) 12 BINARY_SUBSCR 13 STORE_FAST 1 (y) 16 LOAD_CONST 0 (None) 19 RETURN_VALUE