Recientemente me encontré con una extraña discrepancia cuando intentaba ordenar listas autorreferenciales usando .sort()
y sorted()
. Esperaba que alguien pudiera arrojar algo de luz sobre esto. El código en cuestión es el siguiente:
lst = [1, 2, 3] lst[0] = lst lst[1] = lst lst[2] = lst print(lst) print(sorted(lst)) lst.sort() print(lst)
El código anterior produce el siguiente resultado:
[[...], [...], [...]] [[[...], [...], [...]], [[...], [...], [...]], [[...], [...], [...]]] [[...], [...], [...]]
Es la salida de print(sorted(lst))
lo que me desconcierta. ¿Me pregunto si es alguna forma de recursión lo que lo está causando?
Voy a llamar x
a su lista porque, francamente, me l
demasiado al número uno y me está confundiendo. Entonces tienes una lista x
que se ve así
[x, x, x]
Ahora, lo hacemos
print(x)
Python es lo suficientemente inteligente como para decir "oye, mira, esta lista se contiene a sí misma de forma recursiva, no la imprimamos de nuevo dentro de sí misma". Todos los lugares x
que aparecen en su lista, obtenemos [...]
[[...], [...], [...]]
Ahora considera
x.sort() print(x)
Ordenamos la lista, lo que no hace mucho ya que todos los elementos son iguales. Sin embargo, lo más importante es que todo sucede en el lugar . La lista comenzó luciendo como [x, x, x]
y terminará luciendo como [x, x, x]
, donde x
es nuestra lista. Así que la impresión se ve igual.
[[...], [...], [...]]
Finalmente, tu divertido ejemplo.
sorted(x)
sorted
, a diferencia de list.sort
, no modifica la lista y en su lugar produce una nueva lista. Llamemos a esta nueva lista y
. En su ejemplo x.sort
, al final, tenemos la misma lista x
que parece x = [x, x, x]
. Cuando imprimimos la lista, inmediatamente vemos la recursividad y dejamos de imprimir.
Sin embargo, sorted(x)
produce una nueva lista. La lista aún se verá como [x, x, x]
, pero no es la lista x
. Es una nueva lista y = [x, x, x]
.
Ahora, lo hacemos
print(sorted(x))
Python ve una lista de tres elementos: [x, x, x]
. Nos fijamos en cada uno de esos elementos. Estamos imprimiendo y
, por lo que el hecho de que esta lista contenga x
no es un problema de recurrencia; es una lista perfectamente ordinaria que contiene otras listas. Entonces imprimimos x
dentro de y
. Ahora, una capa más abajo, miramos dentro de x
y vemos que contiene, he aquí, x
de nuevo. Ese es un problema de recursión, pero sucedió un paso después, porque hicimos una nueva lista que, aunque parece idéntica a la original, es distinta.
[[[...], [...], [...]], [[...], [...], [...]], [[...], [...], [...]]]
Tal vez pensaste después de lst[0] = lst
que lst
sería [[1,2,3],2,3]
. Si es así, estaría suponiendo que = lst
pasa por valor . Pero las listas se pasan por referencia , por lo que en ese punto lst
es [lst,2,3]
.
Para pasar por valor, use lst[0] = list(lst)
etc. Esta vez el lado derecho crea una nueva lista, con el mismo valor que antes pero con una nueva referencia, ya que en este contexto list(lst)
es sintáctico azúcar por [v for v in lst]
.
Como señaló @DeepSpace, este hecho sobre la list
también explica por qué print(list(lst))
es tres veces más detallado que print(lst)
; imprime [v for v in lst]
, que es solo [lst, lst, lst]
.