Python: Cómo copiar una lista

Objetivo: aplicar las técnicas de slicing de secuencias para obtener un duplicado de una lista.

En el artículo anterior, El sagrado misterio de la inmutabilidad, pudimos apreciar un fenómeno curioso a la hora de trabajar con listas.

Supongamos las siguientes asignaciones aparentemente inocentes:

>>> x = [1, 2]
>>> y = x

Tiene toda la pinta de que y es una copia de x, ¿no?.

>>> y
[1, 2]

Si ahora modificamos la lista x:

>>> x.append(3)
>>> x
[1, 2, 3]

¿Qué habrá sucedido con y?

>>> y
[1, 2, 3]

Atención: ¡los cambios en x también han ocurrido en y! Esto es Python.

Lo que está pasando es que x e y se corresponden con el mismo objeto en memoria. Esto no tendría la menor importancia desde el punto de vista del programador si el objeto en cuestión fuera inmutable, como un número, un string o una tupla, pues no pueden ser modificados in situ. En cambio, con las listas tenemos un serio problema, como hemos podido comprobar.

¿Cómo hacemos, entonces, si necesitamos duplicar una lista de modo que cada una pueda vivir su vida libre independientemente de la otra?

Las técnicas de slicing de secuencias, que ya hemos dejado caer con anterioridad, permiten la independencia de las listas clonadas.

Repasemos brevemente el concepto de slicing, o troceado de una secuencia.

>>> x = 'pradera'
>>> x[1:5]
'rade'

Con esa instrucción extraemos desde el elemento cón índice 1 (recuerda que, en las secuencias, el primer elemento tiene por índice cero) hasta, y sin incluir, el de índice 5; es decir, desde el 1 al 4.

Fíjate en el formato de la instrucción: entre corchetes y separando los límites mediante los dos puntos.

Podemos omitir uno de los índices:

>>> x[:5]
'prade'

que equivale a tomar todos los caracteres desde el principio hasta el de índice cuatro.

>>> x[3:]
'dera'

que es lo mismo que extraer desde el que tiene por índice 3 hasta el último de la secuencia.

O también podemos suprimir los dos, equivalente a extraer todo desde el principio hasta el fin:

>>> x[:]
'pradera'

¿Qué sentido tiene realizar algo así, te preguntarás?

La clave estriba en que siempre que hagamos un slicing de una lista, obtendremos un objeto distinto, aunque sea un slicing como el anterior, de principio a fin.

Obsérvalo:

>>> x = [1, 2]
>>> y = x[:]
>>> y
[1, 2]

Tras esta operación y contiene el resultado de la extracción completa de los elementos de x, pero ahora sí que se trata de un duplicado independiente. Comprobémoslo:

>>> id(x)
162280364
>>> id(y)
162280556

También podríamos haber empleado el operador is para verificar si dos variables apuntan al mismo objeto, lo que es más rápido que comprobar las identidades individuales:

>>> x is y
False

Hemos logrado separar a las listas siamesas. Una última comprobación:

>>> x.append(3)
>>> x
[1, 2, 3]
>>> y
[1, 2]

Ley de vida: x e y dejan de ser almas paralelas y cada una recorre ahora su propio camino hasta que, inevitablemente, llegue el día en que dejen de existir.

Javier Montero Gabarró


http://elclubdelautodidacta.es/wp/2012/09/python-como-copiar-una-lista/


El texto de este artículo se encuentra sometido a una licencia Creative Commons del tipo CC-BY-NC-ND (reconocimiento, no comercial, sin obra derivada, 3.0 unported)


El Club del Autodidacta


Consulta el índice completo de artículos relacionados con Python.

10 comentarios sobre “Python: Cómo copiar una lista”

  1. En la versión que yo tengo (3.3.3.) no funciona así. El objeto sigue siendo el mismo hasta que es modificado.

    >>> x = ‘Burlington’
    >>> y = x[:]
    >>> id(x)
    31136888
    >>> id(y)
    31136888
    >>> x = x + ‘ Loan Management’
    >>> x
    ‘Burlington Loan Management’
    >>> y
    ‘Burlington’
    >>> id(x)
    36856456
    >>> id(y)
    31136888
    >>>

    Gracias por el curso, me está resultando de mucha utilidad!!

    1. Ojo, no te confundas. No es una cuestión de la versión, sino de que tú estás haciendo un duplicado mediante slicing de un string, no de una lista. Su comportamiento es completamente diferente.
      Me alegro de que los artículos te sean útiles. Muchas gracias por pasarte y saludos!

  2. Javier, realmente me salvaste con tu clarísima explicación de la filosofía Python!!! Llevaba todo el día sin entender por qué al tratar de hacer un duplicado de una lista, éstas no seguían “caminos separados”. ¡Muchísimas gracias!. Otra cosa que me llamó la atención hace un tiempo y que aún no sé la razón, es por qué razón cuando computo 0.01*35 me devuelve 0.35000000000000003.
    Saludos y gracias!!!

    1. Buenos días, Enrique:
      Me alegro mucho de que el artículo te haya servido de ayuda. Si has programado en otros lenguajes antes, es importante tomarse el tiempo en entender las peculiaridades de Python respecto a estos.
      Lo segundo no es un problema de Python: bienvenido a la aritmética en punto flotante.
      0,01 no se almacena internamente como 0.01. Prueba a convertirlo a binario, no sale exacto, tiene infinitos términos. Lo único que puede tratar Python de hacer es almacenarlo con la máxima precisión posible.
      Aunque tu veas esto:
      >>> x = 0.01
      >>> x
      >>> 0.01
      Lo que estás viendo no es más que un redondeo que te hace Python, pero no te muestra el valor real que tiene almacenado.
      Eso sí, tan pronto realizas aritmética con ese valor se manifiesta su naturaleza imprecisa, como has observado. Puedes tú redondearla por tu cuenta, o puedes usar módulos como decimal.
      Esé sera objeto de otro artículo… 😉
      Saludos

  3. me pasó que traté de copiar una lista de listas y me arrojaba el mismo problema, ya que las listas internas no quedan independientes.

    a=[ [1,2], [3,4,5] ]
    b=a[:]

    id(a) != id(b)

    pero

    id(a[k]) = id(b[k]) , k=0,…,len(a)-1

    Pero en base a tu idea, pude resolverlo aplicando un for sobre las listas interiores:

    a=[ [1,2], [3,4,5] ]
    b=[ k[:] for k in a]

    con lo anterior, tenemos:

    id(a) != id(b)

    y además

    id(a[k]) != id(b[k]) , k=0,…,len(a)-1

    Saludos y gracias!!!

  4. saludos, y porqué no manejarlas como objetos diferentes con el metodo copy() ?
    lista = [1,2,3,4]
    lista2 = lista.copy()
    así no reventarse la cabeza si es el mismo o no objeto.

Deja un comentario