¿Cuáles son las opciones para clonar o copiar una lista en Python?

Al usar new_list = my_list, cualquier modificación a new_list cambia my_list cada vez. ¿Por qué es esto?

2431
aF. 10 abr. 2010 a las 12:49

16 respuestas

Felix ya proporcionó una excelente respuesta, pero pensé que haría una comparación de velocidad de los diversos métodos:

  1. 10.59 segundos (105.9us / itn) - copy.deepcopy(old_list)
  2. 10.16 sec (101.6us / itn) - método de python puro Copy() copiando clases con copia profunda
  3. 1.488 segundos (14.88us / itn): el método puro de Python Copy() no copia clases (solo dictados / listas / tuplas)
  4. 0.325 segundos (3.25us / itn) - for item in old_list: new_list.append(item)
  5. 0.217 sec (2.17us / itn) - [i for i in old_list] (a comprensión de la lista)
  6. 0.186 segundos (1.86us / itn) - copy.copy(old_list)
  7. 0.075 segundos (0.75us / itn) - list(old_list)
  8. 0.053 segundos (0.53us / itn) - new_list = []; new_list.extend(old_list)
  9. 0.039 sec (0.39us / itn) - old_list[:] (corte de listas )

Entonces, el más rápido es el corte de listas. Pero tenga en cuenta que copy.copy(), list[:] y list(list), a diferencia de copy.deepcopy() y la versión de Python, no copie ninguna lista, diccionario o instancia de clase en la lista, así que si el los originales cambian, también cambiarán en la lista copiada y viceversa.

(Aquí está el script si alguien está interesado o quiere plantear algún problema :)

from copy import deepcopy

class old_class:
    def __init__(self):
        self.blah = 'blah'

class new_class(object):
    def __init__(self):
        self.blah = 'blah'

dignore = {str: None, unicode: None, int: None, type(None): None}

def Copy(obj, use_deepcopy=True):
    t = type(obj)

    if t in (list, tuple):
        if t == tuple:
            # Convert to a list if a tuple to 
            # allow assigning to when copying
            is_tuple = True
            obj = list(obj)
        else: 
            # Otherwise just do a quick slice copy
            obj = obj[:]
            is_tuple = False

        # Copy each item recursively
        for x in xrange(len(obj)):
            if type(obj[x]) in dignore:
                continue
            obj[x] = Copy(obj[x], use_deepcopy)

        if is_tuple: 
            # Convert back into a tuple again
            obj = tuple(obj)

    elif t == dict: 
        # Use the fast shallow dict copy() method and copy any 
        # values which aren't immutable (like lists, dicts etc)
        obj = obj.copy()
        for k in obj:
            if type(obj[k]) in dignore:
                continue
            obj[k] = Copy(obj[k], use_deepcopy)

    elif t in dignore: 
        # Numeric or string/unicode? 
        # It's immutable, so ignore it!
        pass 

    elif use_deepcopy: 
        obj = deepcopy(obj)
    return obj

if __name__ == '__main__':
    import copy
    from time import time

    num_times = 100000
    L = [None, 'blah', 1, 543.4532, 
         ['foo'], ('bar',), {'blah': 'blah'},
         old_class(), new_class()]

    t = time()
    for i in xrange(num_times):
        Copy(L)
    print 'Custom Copy:', time()-t

    t = time()
    for i in xrange(num_times):
        Copy(L, use_deepcopy=False)
    print 'Custom Copy Only Copying Lists/Tuples/Dicts (no classes):', time()-t

    t = time()
    for i in xrange(num_times):
        copy.copy(L)
    print 'copy.copy:', time()-t

    t = time()
    for i in xrange(num_times):
        copy.deepcopy(L)
    print 'copy.deepcopy:', time()-t

    t = time()
    for i in xrange(num_times):
        L[:]
    print 'list slicing [:]:', time()-t

    t = time()
    for i in xrange(num_times):
        list(L)
    print 'list(L):', time()-t

    t = time()
    for i in xrange(num_times):
        [i for i in L]
    print 'list expression(L):', time()-t

    t = time()
    for i in xrange(num_times):
        a = []
        a.extend(L)
    print 'list extend:', time()-t

    t = time()
    for i in xrange(num_times):
        a = []
        for y in L:
            a.append(y)
    print 'list append:', time()-t

    t = time()
    for i in xrange(num_times):
        a = []
        a.extend(i for i in L)
    print 'generator expression extend:', time()-t
580
Georgy 12 may. 2019 a las 14:16

Tenga en cuenta que hay algunos casos en los que si ha definido su propia clase personalizada y desea conservar los atributos, debe utilizar copy.copy() o copy.deepcopy() en lugar de las alternativas, por ejemplo, en Python 3:

import copy

class MyList(list):
    pass

lst = MyList([1,2,3])

lst.name = 'custom list'

d = {
'original': lst,
'slicecopy' : lst[:],
'lstcopy' : lst.copy(),
'copycopy': copy.copy(lst),
'deepcopy': copy.deepcopy(lst)
}


for k,v in d.items():
    print('lst: {}'.format(k), end=', ')
    try:
        name = v.name
    except AttributeError:
        name = 'NA'
    print('name: {}'.format(name))

Salidas:

lst: original, name: custom list
lst: slicecopy, name: NA
lst: lstcopy, name: NA
lst: copycopy, name: custom list
lst: deepcopy, name: custom list
6
Chris_Rands 16 may. 2018 a las 14:31

¿Cuáles son las opciones para clonar o copiar una lista en Python?

En Python 3, se puede hacer una copia superficial con:

a_copy = a_list.copy()

En Python 2 y 3, puede obtener una copia superficial con una porción completa del original:

a_copy = a_list[:]

Explicación

Hay dos formas semánticas para copiar una lista. Una copia superficial crea una nueva lista de los mismos objetos, una copia profunda crea una nueva lista que contiene nuevos objetos equivalentes.

Copia de lista superficial

Una copia superficial solo copia la lista misma, que es un contenedor de referencias a los objetos en la lista. Si los objetos contenidos en sí mismos son mutables y uno se modifica, el cambio se reflejará en ambas listas.

Hay diferentes formas de hacer esto en Python 2 y 3. Las formas de Python 2 también funcionarán en Python 3.

Python 2

En Python 2, la forma idiomática de hacer una copia superficial de una lista es con una porción completa del original:

a_copy = a_list[:]

También puede lograr lo mismo pasando la lista a través del constructor de la lista,

a_copy = list(a_list)

Pero usar el constructor es menos eficiente:

>>> timeit
>>> l = range(20)
>>> min(timeit.repeat(lambda: l[:]))
0.30504298210144043
>>> min(timeit.repeat(lambda: list(l)))
0.40698814392089844

Python 3

En Python 3, las listas obtienen el método list.copy:

a_copy = a_list.copy()

En Python 3.5:

>>> import timeit
>>> l = list(range(20))
>>> min(timeit.repeat(lambda: l[:]))
0.38448613602668047
>>> min(timeit.repeat(lambda: list(l)))
0.6309100328944623
>>> min(timeit.repeat(lambda: l.copy()))
0.38122922903858125

Hacer otro puntero no hace una copia

Usar new_list = my_list luego modifica new_list cada vez que my_list cambia. ¿Por qué es esto?

my_list es solo un nombre que apunta a la lista real en la memoria. Cuando dices new_list = my_list no estás haciendo una copia, solo estás agregando otro nombre que apunta a esa lista original en la memoria. Podemos tener problemas similares cuando hacemos copias de las listas.

>>> l = [[], [], []]
>>> l_copy = l[:]
>>> l_copy
[[], [], []]
>>> l_copy[0].append('foo')
>>> l_copy
[['foo'], [], []]
>>> l
[['foo'], [], []]

La lista es solo una matriz de punteros al contenido, por lo que una copia superficial simplemente copia los punteros, por lo que tiene dos listas diferentes, pero tienen el mismo contenido. Para hacer copias de los contenidos, necesita una copia profunda.

Copias profundas

Para hacer una copia profunda de una lista, en Python 2 o 3, use deepcopy en Módulo copy:

import copy
a_deep_copy = copy.deepcopy(a_list)

Para demostrar cómo esto nos permite hacer nuevas sublistas:

>>> import copy
>>> l
[['foo'], [], []]
>>> l_deep_copy = copy.deepcopy(l)
>>> l_deep_copy[0].pop()
'foo'
>>> l_deep_copy
[[], [], []]
>>> l
[['foo'], [], []]

Y entonces vemos que la lista copiada en profundidad es una lista completamente diferente de la original. Podrías rodar tu propia función, pero no lo hagas. Es probable que cree errores que de otro modo no tendría al usar la función de copia profunda de la biblioteca estándar.

No uses eval

Puede ver esto usado como una forma de hacer una copia profunda, pero no lo haga:

problematic_deep_copy = eval(repr(a_list))
  1. Es peligroso, especialmente si está evaluando algo de una fuente en la que no confía.
  2. No es confiable, si un subelemento que está copiando no tiene una representación que pueda evaluarse para reproducir un elemento equivalente.
  3. También es menos eficiente.

En Python 2.7 de 64 bits:

>>> import timeit
>>> import copy
>>> l = range(10)
>>> min(timeit.repeat(lambda: copy.deepcopy(l)))
27.55826997756958
>>> min(timeit.repeat(lambda: eval(repr(l))))
29.04534101486206

En Python 3.5 de 64 bits:

>>> import timeit
>>> import copy
>>> l = list(range(10))
>>> min(timeit.repeat(lambda: copy.deepcopy(l)))
16.84255409205798
>>> min(timeit.repeat(lambda: eval(repr(l))))
34.813894678023644
124
Aaron Hall 9 ene. 2018 a las 15:33

Todos los demás contribuyentes dieron excelentes respuestas, que funcionan cuando tiene una lista de una sola dimensión (nivelada), sin embargo, de los métodos mencionados hasta ahora, solo copy.deepcopy() funciona para clonar / copiar una lista y que no apunte a los objetos anidados list cuando trabaje con listas anidadas multidimensionales (lista de listas). Si bien Felix Kling se refiere a él en su respuesta, hay un poco más sobre el problema y posiblemente una solución alternativa usando incorporados que podrían ser una alternativa más rápida a deepcopy.

Mientras que new_list = old_list[:], copy.copy(old_list)' y para Py3k old_list.copy() funcionan para listas de un solo nivel, vuelven a apuntar a los objetos list anidados dentro de old_list y { {X5}}, y los cambios en uno de los objetos list se perpetúan en el otro.

Editar: Nueva información sacada a la luz

Como lo señalaron Aaron Hall y PM 2Ring usar eval() no solo es una mala idea, también es mucho más lenta que copy.deepcopy().

Esto significa que para las listas multidimensionales, la única opción es copy.deepcopy(). Dicho esto, realmente no es una opción, ya que el rendimiento va al sur cuando intentas usarlo en una matriz multidimensional de tamaño moderado. Traté de timeit usando una matriz de 42x42, no tan desconocida o incluso tan grande para aplicaciones de bioinformática, y dejé de esperar una respuesta y comencé a escribir mi edición en esta publicación.

Parece que la única opción real es inicializar varias listas y trabajar en ellas de forma independiente. Si alguien tiene alguna otra sugerencia sobre cómo manejar la copia de listas multidimensionales, se agradecería.

Como han dicho otros, hay problemas de rendimiento importantes al usar el módulo copy y copy.deepcopy para listas multidimensionales .

18
Yvette Colomb 13 may. 2019 a las 04:09

Comencemos desde el principio y exploremos esta pregunta.

Supongamos que tiene dos listas:

list_1=['01','98']
list_2=[['01','98']]

Y tenemos que copiar ambas listas, ahora comenzando desde la primera lista:

Así que primero intentemos configurando la variable copy en nuestra lista original, list_1:

copy=list_1

Ahora, si está pensando que la copia copió la lista_1, entonces está equivocado. La función id puede mostrarnos si dos variables pueden apuntar al mismo objeto. Intentemos esto:

print(id(copy))
print(id(list_1))

La salida es:

4329485320
4329485320

Ambas variables son exactamente el mismo argumento. ¿Estás sorprendido?

Entonces, como sabemos, python no almacena nada en una variable, las variables solo hacen referencia al objeto y el objeto almacena el valor. Aquí el objeto es un list pero creamos dos referencias a ese mismo objeto con dos nombres de variables diferentes. Esto significa que ambas variables apuntan al mismo objeto, solo que con diferentes nombres.

Cuando haces copy=list_1, en realidad está haciendo:

enter image description here

Aquí en la imagen list_1 y copy hay dos nombres de variables, pero el objeto es el mismo para ambas variables, que es list

Entonces, si intenta modificar la lista copiada, también modificará la lista original porque la lista es solo una, modificará esa lista sin importar lo que haga desde la lista copiada o desde la lista original:

copy[0]="modify"

print(copy)
print(list_1)

Salida:

['modify', '98']
['modify', '98']

Entonces modificó la lista original:

Ahora pasemos a un método pitónico para copiar listas.

copy_1=list_1[:]

Este método soluciona el primer problema que tuvimos:

print(id(copy_1))
print(id(list_1))

4338792136
4338791432

Entonces, como podemos ver, nuestra lista tiene una identificación diferente y significa que ambas variables apuntan a objetos diferentes. Entonces, lo que realmente está sucediendo aquí es:

enter image description here

Ahora intentemos modificar la lista y veamos si aún enfrentamos el problema anterior:

copy_1[0]="modify"

print(list_1)
print(copy_1)

La salida es:

['01', '98']
['modify', '98']

Como puede ver, solo modificó la lista copiada. Eso significa que funcionó.

¿Crees que hemos terminado? No. Intentemos copiar nuestra lista anidada.

copy_2=list_2[:]

list_2 debe hacer referencia a otro objeto que es una copia de list_2. Vamos a revisar:

print(id((list_2)),id(copy_2))

Nosotras obtenemos la salida:

4330403592 4330403528

Ahora podemos suponer que ambas listas apuntan a un objeto diferente, así que ahora tratemos de modificarlo y veamos que está dando lo que queremos:

copy_2[0][1]="modify"

print(list_2,copy_2)

Esto nos da la salida:

[['01', 'modify']] [['01', 'modify']]

Esto puede parecer un poco confuso, porque el mismo método que utilizamos anteriormente funcionó. Tratemos de entender esto.

Cuando lo haga:

copy_2=list_2[:]

Solo está copiando la lista externa, no la lista interna. Podemos usar la función id una vez más para verificar esto.

print(id(copy_2[0]))
print(id(list_2[0]))

La salida es:

4329485832
4329485832

Cuando hacemos copy_2=list_2[:], esto sucede:

enter image description here

Crea la copia de la lista pero solo la copia de la lista externa, no la copia de la lista anidada, la lista anidada es la misma para ambas variables, por lo que si intenta modificar la lista anidada, también modificará la lista original ya que el objeto de la lista anidada es el mismo para ambas listas

¿Cuál es la solución? La solución es la función deepcopy.

from copy import deepcopy
deep=deepcopy(list_2)

Vamos a ver esto:

print(id((list_2)),id(deep))

4322146056 4322148040

Ambas listas externas tienen ID diferentes, intentemos esto en las listas anidadas internas.

print(id(deep[0]))
print(id(list_2[0]))

La salida es:

4322145992
4322145800

Como puede ver, ambas ID son diferentes, lo que significa que podemos suponer que ambas listas anidadas apuntan a objetos diferentes ahora.

Esto significa que cuando haces deep=deepcopy(list_2) lo que realmente sucede:

enter image description here

Ambas listas anidadas apuntan a un objeto diferente y ahora tienen una copia separada de la lista anidada.

Ahora intentemos modificar la lista anidada y ver si resolvió el problema anterior o no:

deep[0][1]="modify"
print(list_2,deep)

Emite:

[['01', '98']] [['01', 'modify']]

Como puede ver, no modificó la lista anidada original, solo modificó la lista copiada.

Si le gusta mi respuesta detallada, le agradecería que la votara. Si tiene alguna duda sobre esta respuesta, puede comentarla a continuación.

27
Corman 7 feb. 2020 a las 22:24

Utilice thing[:]

>>> a = [1,2]
>>> b = a[:]
>>> a += [3]
>>> a
[1, 2, 3]
>>> b
[1, 2]
>>> 
36
Paul Tarjan 10 abr. 2010 a las 08:53

El idioma de Python para hacer esto es newList = oldList[:]

32
erisco 10 abr. 2010 a las 08:53

Quería publicar algo un poco diferente a algunas de las otras respuestas. Aunque es probable que esta no sea la opción más comprensible o más rápida, proporciona una visión interna de cómo funciona la copia profunda, además de ser otra opción alternativa para la copia profunda. Realmente no importa si mi función tiene errores, ya que el objetivo de esto es mostrar una forma de copiar objetos como las respuestas a las preguntas, pero también usar esto como un punto para explicar cómo funciona la copia profunda en su núcleo.

El núcleo de cualquier función de copia profunda es la forma de hacer una copia superficial. ¿Cómo? Simple. Cualquier función de copia profunda solo duplica los contenedores de objetos inmutables. Cuando copia en profundidad una lista anidada, solo está duplicando las listas externas, no los objetos mutables dentro de las listas. Solo estás duplicando los contenedores. Lo mismo funciona para las clases, también. Cuando copia en profundidad una clase, copia en profundidad todos sus atributos mutables. ¿Así que cómo? ¿Cómo es que solo tiene que copiar los contenedores, como listas, dictados, tuplas, iters, clases e instancias de clase?

Es simple. Un objeto mutable no se puede duplicar realmente. Nunca se puede cambiar, por lo que es solo un valor único. Eso significa que nunca tendrá que duplicar cadenas, números, bools o ninguno de esos. ¿Pero cómo duplicaría los contenedores? Simple. Solo debe inicializar un nuevo contenedor con todos los valores. Deepcopy se basa en la recursividad. Duplica todos los contenedores, incluso aquellos con contenedores dentro de ellos, hasta que no queden contenedores. Un contenedor es un objeto inmutable.

Una vez que sepa eso, duplicar completamente un objeto sin ninguna referencia es bastante fácil. Aquí hay una función para hacer una copia en profundidad de los tipos de datos básicos (no funcionaría para clases personalizadas, pero siempre puede agregar eso)

def deepcopy(x):
  immutables = (str, int, bool, float)
  mutables = (list, dict, tuple)
  if isinstance(x, immutables):
    return x
  elif isinstance(x, mutables):
    if isinstance(x, tuple):
      return tuple(deepcopy(list(x)))
    elif isinstance(x, list):
      return [deepcopy(y) for y in x]
    elif isinstance(x, dict):
      values = [deepcopy(y) for y in list(x.values())]
      keys = list(x.keys())
      return dict(zip(keys, values))

La copia profunda integrada de Python se basa en ese ejemplo. La única diferencia es que admite otros tipos, y también admite clases de usuario al duplicar los atributos en una nueva clase duplicada, y también bloquea la recursión infinita con una referencia a un objeto que ya se ha visto usando una lista de notas o un diccionario. Y eso es realmente para hacer copias profundas. En esencia, hacer una copia profunda es solo hacer copias superficiales. Espero que esta respuesta agregue algo a la pregunta.

EJEMPLOS

Digamos que tiene esta lista: [1, 2, 3] . Los números inmutables no pueden duplicarse, pero la otra capa sí. Puede duplicarlo utilizando una lista de comprensión: [x para x en [1, 2, 3]

Ahora, imagine que tiene esta lista: [[1, 2], [3, 4], [5, 6]] . Esta vez, desea hacer una función, que utiliza la recursividad para copiar en profundidad todas las capas de la lista. En lugar de la comprensión de la lista anterior:

[x for x in _list]

Utiliza uno nuevo para las listas:

[deepcopy_list(x) for x in _list]

Y deepcopy_list se ve así:

def deepcopy_list(x):
  if isinstance(x, (str, bool, float, int)):
    return x
  else:
    return [deepcopy_list(y) for y in x]

Entonces ahora tiene una función que puede hacer una copia en profundidad de cualquier lista de strs, bools, floast, ints e incluso listas en infinitas capas usando recursividad. Y ahí lo tienes, copiando en profundidad.

TLDR : Deepcopy utiliza la recursividad para duplicar objetos y simplemente devuelve los mismos objetos inmutables que antes, ya que los objetos inmutables no pueden duplicarse. Sin embargo, copia en profundidad las capas más internas de objetos mutables hasta que alcanza la capa mutable más externa de un objeto.

1
Corman 8 sep. 2019 a las 02:25

Una ligera perspectiva práctica para mirar en la memoria a través de id y gc.

>>> b = a = ['hell', 'word']
>>> c = ['hell', 'word']

>>> id(a), id(b), id(c)
(4424020872, 4424020872, 4423979272) 
     |           |
      -----------

>>> id(a[0]), id(b[0]), id(c[0])
(4424018328, 4424018328, 4424018328) # all referring to same 'hell'
     |           |           |
      -----------------------

>>> id(a[0][0]), id(b[0][0]), id(c[0][0])
(4422785208, 4422785208, 4422785208) # all referring to same 'h'
     |           |           |
      -----------------------

>>> a[0] += 'o'
>>> a,b,c
(['hello', 'word'], ['hello', 'word'], ['hell', 'word'])  # b changed too
>>> id(a[0]), id(b[0]), id(c[0])
(4424018384, 4424018384, 4424018328) # augmented assignment changed a[0],b[0]
     |           |
      -----------

>>> b = a = ['hell', 'word']
>>> id(a[0]), id(b[0]), id(c[0])
(4424018328, 4424018328, 4424018328) # the same hell
     |           |           |
      -----------------------

>>> import gc
>>> gc.get_referrers(a[0]) 
[['hell', 'word'], ['hell', 'word']]  # one copy belong to a,b, the another for c
>>> gc.get_referrers(('hell'))
[['hell', 'word'], ['hell', 'word'], ('hell', None)] # ('hello', None) 
1
B.Mr.W. 23 nov. 2019 a las 19:01

Me sorprende que esto no se haya mencionado aún, así que en aras de la integridad ...

Puede realizar el desempaquetado de la lista con el "operador splat": *, que también copiará elementos de su lista.

old_list = [1, 2, 3]

new_list = [*old_list]

new_list.append(4)
old_list == [1, 2, 3]
new_list == [1, 2, 3, 4]

La desventaja obvia de este método es que solo está disponible en Python 3.5+.

Sin embargo, esto parece funcionar mejor que otros métodos comunes.

x = [random.random() for _ in range(1000)]

%timeit a = list(x)
%timeit a = x.copy()
%timeit a = x[:]

%timeit a = [*x]

#: 2.47 µs ± 38.1 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
#: 2.47 µs ± 54.6 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
#: 2.39 µs ± 58.2 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)

#: 2.22 µs ± 43.2 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
11
SCB 26 feb. 2018 a las 02:33

Faltaba un enfoque muy simple independiente de la versión de Python en las respuestas ya dadas que puede usar la mayoría de las veces (al menos lo hago):

new_list = my_list * 1       #Solution 1 when you are not using nested lists

Sin embargo, si my_list contiene otros contenedores (por ejemplo, listas anidadas), debe usar la copia profunda como otros sugirieron en las respuestas anteriores de la biblioteca de copias. Por ejemplo:

import copy
new_list = copy.deepcopy(my_list)   #Solution 2 when you are using nested lists

. Bonus : si no desea copiar elementos, use (también conocido como copia superficial):

new_list = my_list[:]

Comprendamos la diferencia entre la Solución n. ° 1 y la Solución n. ° 2

>>> a = range(5)
>>> b = a*1
>>> a,b
([0, 1, 2, 3, 4], [0, 1, 2, 3, 4])
>>> a[2] = 55 
>>> a,b
([0, 1, 55, 3, 4], [0, 1, 2, 3, 4])

Como puede ver, la Solución # 1 funcionó perfectamente cuando no estábamos usando las listas anidadas. Veamos qué sucederá cuando apliquemos la solución n. ° 1 a las listas anidadas.

>>> from copy import deepcopy
>>> a = [range(i,i+4) for i in range(3)]
>>> a
[[0, 1, 2, 3], [1, 2, 3, 4], [2, 3, 4, 5]]
>>> b = a*1
>>> c = deepcopy(a)
>>> for i in (a, b, c): print i   
[[0, 1, 2, 3], [1, 2, 3, 4], [2, 3, 4, 5]]
[[0, 1, 2, 3], [1, 2, 3, 4], [2, 3, 4, 5]]
[[0, 1, 2, 3], [1, 2, 3, 4], [2, 3, 4, 5]]
>>> a[2].append('99')
>>> for i in (a, b, c): print i   
[[0, 1, 2, 3], [1, 2, 3, 4], [2, 3, 4, 5, 99]]
[[0, 1, 2, 3], [1, 2, 3, 4], [2, 3, 4, 5, 99]]   #Solution#1 didn't work in nested list
[[0, 1, 2, 3], [1, 2, 3, 4], [2, 3, 4, 5]]       #Solution #2 - DeepCopy worked in nested list
6
jainashish 1 nov. 2017 a las 08:08
new_list = my_list[:]

new_list = my_list Intenta entender esto. Digamos que my_list está en la memoria de almacenamiento dinámico en la ubicación X, es decir, my_list apunta a la X. Ahora al asignar new_list = my_list estás dejando que new_list apunte a la X. Esto se conoce como copia superficial.

Ahora, si asigna new_list = my_list[:], simplemente está copiando cada objeto de my_list en new_list. Esto se conoce como copia profunda.

La otra forma en que puede hacer esto es:

  • new_list = list(old_list)
  • import copy new_list = copy.deepcopy(old_list)
3
Ravi Shankar 26 jun. 2017 a las 21:03

Python 3.6 Tiempos

Aquí están los resultados de sincronización usando Python 3.6.8. Tenga en cuenta que estos tiempos son relativos entre sí, no absolutos.

Me limité a hacer solo copias superficiales, y también agregué algunos métodos nuevos que no eran posibles en Python2, como list.copy() (Python3 rebanada equivalente) y dos formas de desempaquetar la lista (*new_list, = list y new_list = [*list]):

METHOD                  TIME TAKEN
b = [*a]                2.75180600000021
b = a * 1               3.50215399999990
b = a[:]                3.78278899999986  # Python2 winner (see above)
b = a.copy()            4.20556500000020  # Python3 "slice equivalent" (see above)
b = []; b.extend(a)     4.68069800000012
b = a[0:len(a)]         6.84498999999959
*b, = a                 7.54031799999984
b = list(a)             7.75815899999997
b = [i for i in a]      18.4886440000000
b = copy.copy(a)        18.8254879999999
b = []
for item in a:
  b.append(item)        35.4729199999997

Podemos ver que el ganador de Python2 todavía lo hace bien, pero no supera a Python3 list.copy() por mucho, especialmente teniendo en cuenta la legibilidad superior de este último.

El caballo oscuro es el método de desempaque y reempaque (b = [*a]), que es ~ 25% más rápido que el corte en bruto, y más del doble de rápido que el otro método de desempaque (*b, = a).

b = a * 1 también lo hace sorprendentemente bien.

Tenga en cuenta que estos métodos no generan resultados equivalentes para cualquier entrada que no sea listas. Todos funcionan para objetos divisibles, algunos funcionan para cualquier iterable, pero solo {{X0} } funciona para objetos Python más generales.


Aquí está el código de prueba para las partes interesadas (Plantilla desde aquí ):

import timeit

COUNT = 50000000
print("Array duplicating. Tests run", COUNT, "times")
setup = 'a = [0,1,2,3,4,5,6,7,8,9]; import copy'

print("b = list(a)\t\t", timeit.timeit(stmt='b = list(a)', setup=setup, number=COUNT))
print("b = copy.copy(a)\t", timeit.timeit(stmt='b = copy.copy(a)', setup=setup, number=COUNT))
print("b = a.copy()\t\t", timeit.timeit(stmt='b = a.copy()', setup=setup, number=COUNT))
print("b = a[:]\t\t", timeit.timeit(stmt='b = a[:]', setup=setup, number=COUNT))
print("b = a[0:len(a)]\t\t", timeit.timeit(stmt='b = a[0:len(a)]', setup=setup, number=COUNT))
print("*b, = a\t\t\t", timeit.timeit(stmt='*b, = a', setup=setup, number=COUNT))
print("b = []; b.extend(a)\t", timeit.timeit(stmt='b = []; b.extend(a)', setup=setup, number=COUNT))
print("b = []; for item in a: b.append(item)\t", timeit.timeit(stmt='b = []\nfor item in a:  b.append(item)', setup=setup, number=COUNT))
print("b = [i for i in a]\t", timeit.timeit(stmt='b = [i for i in a]', setup=setup, number=COUNT))
print("b = [*a]\t\t", timeit.timeit(stmt='b = [*a]', setup=setup, number=COUNT))
print("b = a * 1\t\t", timeit.timeit(stmt='b = a * 1', setup=setup, number=COUNT))
29
River 14 may. 2019 a las 02:54

Ya hay muchas respuestas que le dicen cómo hacer una copia adecuada, pero ninguna de ellas dice por qué falló su 'copia' original.

Python no almacena valores en variables; une nombres a objetos. Su asignación original tomó el objeto al que hace referencia my_list y también lo vinculó a new_list. Independientemente del nombre que utilice, todavía hay una sola lista, por lo que los cambios realizados al referirse a ella como my_list persistirán al referirse a ella como new_list. Cada una de las otras respuestas a esta pregunta le brinda diferentes formas de crear un nuevo objeto para vincular a new_list.

Cada elemento de una lista actúa como un nombre, ya que cada elemento se une no exclusivamente a un objeto. Una copia superficial crea una nueva lista cuyos elementos se unen a los mismos objetos que antes.

new_list = list(my_list)  # or my_list[:], but I prefer this syntax
# is simply a shorter way of:
new_list = [element for element in my_list]

Para llevar su copia de la lista un paso más allá, copie cada objeto al que hace referencia su lista y vincule esas copias de elementos a una nueva lista.

import copy  
# each element must have __copy__ defined for this...
new_list = [copy.copy(element) for element in my_list]

Esto aún no es una copia profunda, porque cada elemento de una lista puede referirse a otros objetos, al igual que la lista está vinculada a sus elementos. Para copiar recursivamente cada elemento de la lista, y luego cada uno de los objetos mencionados por cada elemento, y así sucesivamente: realice una copia profunda.

import copy
# each element must have __deepcopy__ defined for this...
new_list = copy.deepcopy(my_list)

Consulte la documentación para obtener más información sobre casos de esquina en la copia.

52
jack 19 ene. 2018 a las 16:00

me han dicho que Python 3.3+ agrega el método list.copy(), que debería ser tan rápido como cortar:

newlist = old_list.copy()

145
River 5 abr. 2017 a las 01:13

Recuerda eso en Python cuando haces:

    list1 = ['apples','bananas','pineapples']
    list2 = list1

List2 no almacena la lista real, sino una referencia a list1. Entonces, cuando haces algo para list1, list2 también cambia. use el módulo de copia (no predeterminado, descarga en pip) para hacer una copia original de la lista (copy.copy() para listas simples, copy.deepcopy() para las anidadas). Esto hace una copia que no cambia con la primera lista.

0
Dr. Hippo 22 feb. 2020 a las 12:44
2612802