Business
Jobs
  • About Us
  • Solutions
    • Job Postings
      Post your job and receive qualified candidates in 48h.
    • Candidate Assessments
      500+ technical and psychological tests, plus anti-fraud.
    • Headhunting
      Tailor-made executive search from start to finish.
    • Payroll + EOR
      Payroll dispersal and EOR across 15+ LATAM countries.
  • Pricing
  • Jobs

0

257
Views
¿Qué hace que [*a] sobreasigne?

Aparentemente list(a) no se sobreasigna, [x for x in a] se sobreasigna en algunos puntos, y [*a] se sobreasigna todo el tiempo .

Tamaños hasta n=100

Aquí están los tamaños n de 0 a 12 y los tamaños resultantes en bytes para los tres métodos:

 0 56 56 56 1 64 88 88 2 72 88 96 3 80 88 104 4 88 88 112 5 96 120 120 6 104 120 128 7 112 120 136 8 120 120 152 9 128 184 184 10 136 184 192 11 144 184 200 12 152 184 208

Calculado así, reproducible en repl.it , usando Python 3.8 :

 from sys import getsizeof for n in range(13): a = [None] * n print(n, getsizeof(list(a)), getsizeof([x for x in a]), getsizeof([*a]))

¿Entonces, cómo funciona esto? ¿Cómo sobreasigna [*a] ? En realidad, ¿qué mecanismo utiliza para crear la lista de resultados a partir de la entrada dada? ¿Utiliza un iterador sobre a y usa algo como list.append ? ¿Dónde está el código fuente?

( Colaboración con los datos y el código que produjo las imágenes).

Acercando a n más pequeño:

Tamaños hasta n=40

Alejar a n más grande:

Tamaños hasta n=1000

over 4 years ago · Santiago Trujillo
3 answers
Answer question

0

[*a] está haciendo internamente el equivalente en C de :

  1. Hacer una nueva list vacía
  2. Llame newlist.extend(a)
  3. list devoluciones.

Entonces, si expande su prueba a:

 from sys import getsizeof for n in range(13): a = [None] * n l = [] l.extend(a) print(n, getsizeof(list(a)), getsizeof([x for x in a]), getsizeof([*a]), getsizeof(l))

¡Pruébelo en línea!

verá los resultados de getsizeof([*a]) y l = []; l.extend(a); getsizeof(l) son iguales.

Esto suele ser lo correcto; cuando se extend , por lo general se espera agregar más más tarde, y de manera similar para el desempaquetado generalizado, se supone que se agregarán varias cosas una tras otra. [*a] no es el caso normal; Python asume que se agregan varios elementos o iterables a la list ( [*a, b, c, *d] ), por lo que la sobreasignación ahorra trabajo en el caso común.

Por el contrario, una list construida a partir de un solo iterable de tamaño predeterminado (con list() ) puede no crecer ni reducirse durante el uso, y la sobreasignación es prematura hasta que se demuestre lo contrario; Python solucionó recientemente un error que hacía que el constructor sobreasignara incluso para entradas con tamaño conocido .

En cuanto a las comprensiones de list , son efectivamente equivalentes a los append repetidos, por lo que está viendo el resultado final del patrón de crecimiento de sobreasignación normal al agregar un elemento a la vez.

Para ser claros, nada de esto es una garantía de idioma. Así es como lo implementa CPython. La especificación del lenguaje Python generalmente no se preocupa por los patrones de crecimiento específicos en la list (aparte de garantizar la amortización de O(1) append s y pop s desde el final). Como se señaló en los comentarios, la implementación específica cambia nuevamente en 3.9; si bien no afectará a [*a] , podría afectar otros casos en los que lo que solía ser "crear una tuple temporal de elementos individuales y luego extend con la tuple " ahora se convierte en múltiples aplicaciones de LIST_APPEND , que pueden cambiar cuando se produce la sobreasignación y qué números entran en el cálculo.

over 4 years ago · Santiago Trujillo Report

0

Imagen completa de lo que sucede, basándose en las otras respuestas y comentarios (especialmente la respuesta de ShadowRanger , que también explica por qué se hace así).

El desmontaje muestra que se usa BUILD_LIST_UNPACK :

 >>> import dis >>> dis.dis('[*a]') 1 0 LOAD_NAME 0 (a) 2 BUILD_LIST_UNPACK 1 4 RETURN_VALUE

Eso se maneja en ceval.c , que construye una lista vacía y la extiende (con a ):

 case TARGET(BUILD_LIST_UNPACK): { ... PyObject *sum = PyList_New(0); ... none_val = _PyList_Extend((PyListObject *)sum, PEEK(i));

_PyList_Extend usa list_extend :

 _PyList_Extend(PyListObject *self, PyObject *iterable) { return list_extend(self, iterable); }

Que llama a list_resize con la suma de los tamaños :

 list_extend(PyListObject *self, PyObject *iterable) ... n = PySequence_Fast_GET_SIZE(iterable); ... m = Py_SIZE(self); ... if (list_resize(self, m + n) < 0) {

Y eso sobreasigna de la siguiente manera:

 list_resize(PyListObject *self, Py_ssize_t newsize) { ... new_allocated = (size_t)newsize + (newsize >> 3) + (newsize < 9 ? 3 : 6);

Comprobemos eso. Calcule el número esperado de puntos con la fórmula anterior y calcule el tamaño de bytes esperado multiplicándolo por 8 (ya que aquí estoy usando Python de 64 bits) y agregando el tamaño de bytes de una lista vacía (es decir, la sobrecarga constante de un objeto de lista) :

 from sys import getsizeof for n in range(13): a = [None] * n expected_spots = n + (n >> 3) + (3 if n < 9 else 6) expected_bytesize = getsizeof([]) + expected_spots * 8 real_bytesize = getsizeof([*a]) print(n, expected_bytesize, real_bytesize, real_bytesize == expected_bytesize)

Producción:

 0 80 56 False 1 88 88 True 2 96 96 True 3 104 104 True 4 112 112 True 5 120 120 True 6 128 128 True 7 136 136 True 8 152 152 True 9 184 184 True 10 192 192 True 11 200 200 True 12 208 208 True

Coincide excepto por n = 0 , que list_extend en realidad abrevia , por lo que en realidad también coincide:

 if (n == 0) { ... Py_RETURN_NONE; } ... if (list_resize(self, m + n) < 0) {
over 4 years ago · Santiago Trujillo Report

0

Estos serán los detalles de implementación del intérprete de CPython y, por lo tanto, es posible que no sean coherentes entre otros intérpretes.

Dicho esto, puede ver dónde entran los comportamientos de comprensión y list(a) aquí:

https://github.com/python/cpython/blob/master/Objects/listobject.c#L36

Específicamente para la comprensión:

 * The growth pattern is: 0, 4, 8, 16, 25, 35, 46, 58, 72, 88, ... ... new_allocated = (size_t)newsize + (newsize >> 3) + (newsize < 9 ? 3 : 6);

Justo debajo de esas líneas, está list_preallocate_exact que se usa cuando se llama a list(a) .

over 4 years ago · Santiago Trujillo Report
Answer question
Find remote jobs

Discover the new way to find a job!

Top jobs
Top job categories
Business
Post vacancy Pricing Sales
Legal
Terms and conditions Privacy policy
© 2026 PeakU Inc. All Rights Reserved.
Andres GPT
Show me some job opportunities
There's an error!