Quelles sont les options pour cloner ou copier une liste en Python?

Lors de l'utilisation de new_list = my_list, toute modification de new_list change my_list à chaque fois. Pourquoi est-ce?

2431
aF. 10 avril 2010 à 12:49

16 réponses

Felix a déjà fourni une excellente réponse, mais j'ai pensé que je ferais une comparaison rapide des différentes méthodes:

  1. 10,59 s (105,9us / itn) - copy.deepcopy(old_list)
  2. 10.16 sec (101.6us / itn) - méthode python Copy() pure copie des classes avec deepcopy
  3. 1.488 sec (14.88us / itn) - méthode pure python Copy() ne copiant pas les classes (seulement les dict / listes / tuples)
  4. 0,325 sec (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] (un compréhension de liste)
  6. 0,186 s (1.86us / itn) - copy.copy(old_list)
  7. 0,075 s (0,75us / itn) - list(old_list)
  8. 0,053 sec (0,53us / itn) - new_list = []; new_list.extend(old_list)
  9. 0,039 sec (0.39us / itn) - old_list[:] (découpage de liste )

Le découpage de liste est donc le plus rapide. Mais sachez que copy.copy(), list[:] et list(list), contrairement à copy.deepcopy() et à la version python, ne copient pas de listes, dictionnaires et instances de classe dans la liste, donc si le les originaux changent, ils changeront aussi dans la liste copiée et vice versa.

(Voici le script si quelqu'un est intéressé ou souhaite soulever des problèmes :)

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 mai 2019 à 14:16

Notez que dans certains cas, si vous avez défini votre propre classe personnalisée et que vous souhaitez conserver les attributs, vous devez utiliser copy.copy() ou copy.deepcopy() plutôt que les alternatives, par exemple 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))

Les sorties:

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 mai 2018 à 14:31

Quelles sont les options pour cloner ou copier une liste en Python?

En Python 3, une copie superficielle peut être faite avec:

a_copy = a_list.copy()

En Python 2 et 3, vous pouvez obtenir une copie superficielle avec une tranche complète de l'original:

a_copy = a_list[:]

Explication

Il existe deux façons sémantiques de copier une liste. Une copie superficielle crée une nouvelle liste des mêmes objets, une copie complète crée une nouvelle liste contenant de nouveaux objets équivalents.

Copie de liste peu profonde

Une copie superficielle copie uniquement la liste elle-même, qui est un conteneur de références aux objets de la liste. Si les objets qu'ils contiennent sont modifiables et qu'un est modifié, le changement sera reflété dans les deux listes.

Il existe différentes façons de le faire en Python 2 et 3. Les méthodes Python 2 fonctionneront également en Python 3.

Python 2

En Python 2, la façon idiomatique de faire une copie superficielle d'une liste est avec une tranche complète de l'original:

a_copy = a_list[:]

Vous pouvez également accomplir la même chose en passant la liste via le constructeur de liste,

a_copy = list(a_list)

Mais l'utilisation du constructeur est moins efficace:

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

Python 3

En Python 3, les listes obtiennent la méthode 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

Faire un autre pointeur ne pas fait une copie

L'utilisation de new_list = my_list modifie ensuite new_list à chaque fois que my_list change. Pourquoi est-ce?

my_list n'est qu'un nom qui pointe vers la liste réelle en mémoire. Lorsque vous dites new_list = my_list que vous ne faites pas de copie, vous ajoutez simplement un autre nom qui pointe vers cette liste d'origine en mémoire. Nous pouvons rencontrer des problèmes similaires lorsque nous faisons des copies de listes.

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

La liste n'est qu'un tableau de pointeurs vers le contenu, donc une copie superficielle copie simplement les pointeurs, et vous avez donc deux listes différentes, mais elles ont le même contenu. Pour faire des copies du contenu, vous avez besoin d'une copie complète.

Copies complètes

Pour créer une copie complète d'une liste, en Python 2 ou 3, utilisez deepcopy dans le Module copy:

import copy
a_deep_copy = copy.deepcopy(a_list)

Pour montrer comment cela nous permet de créer de nouvelles sous-listes:

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

Et nous voyons donc que la liste copiée en profondeur est une liste entièrement différente de l'original. Vous pouvez lancer votre propre fonction - mais ne le faites pas. Vous êtes susceptible de créer des bogues que vous n'auriez pas autrement en utilisant la fonction de copie profonde de la bibliothèque standard.

N'utilisez pas eval

Vous pouvez voir cela comme un moyen de réaliser une copie profonde, mais ne le faites pas:

problematic_deep_copy = eval(repr(a_list))
  1. C'est dangereux, surtout si vous évaluez quelque chose à partir d'une source en laquelle vous n'avez pas confiance.
  2. Ce n'est pas fiable, si un sous-élément que vous copiez n'a pas de représentation qui peut être évaluée pour reproduire un élément équivalent.
  3. C'est aussi moins performant.

En Python 2.7 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

Sur Python 3.5 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 janv. 2018 à 15:33

Tous les autres contributeurs ont donné de bonnes réponses, qui fonctionnent lorsque vous avez une liste à une seule dimension (mise à niveau), mais parmi les méthodes mentionnées jusqu'à présent, seul copy.deepcopy() fonctionne pour cloner / copier une liste et ne pas le faire pointer vers les objets list imbriqués lorsque vous travaillez avec des listes imbriquées multidimensionnelles (liste de listes). Alors que Felix Kling y fait référence dans sa réponse, il y a un peu plus sur le problème et peut-être une solution de contournement en utilisant intégrées qui pourraient s'avérer une alternative plus rapide à deepcopy.

Alors que new_list = old_list[:], copy.copy(old_list)' et pour Py3k old_list.copy() fonctionnent pour les listes à niveau unique, ils reviennent à pointer sur les list objets imbriqués dans les old_list et les { {X5}} et les modifications apportées à l'un des list objets sont perpétuées dans l'autre.

Edit: De nouvelles informations mises à jour

Comme cela a été souligné par Aaron Hall et PM 2Ring l'utilisation de eval() n'est pas seulement une mauvaise idée, elle est également beaucoup plus lente que copy.deepcopy().

Cela signifie que pour les listes multidimensionnelles, la seule option est copy.deepcopy(). Cela étant dit, ce n'est vraiment pas une option car les performances vont bien au sud lorsque vous essayez de les utiliser sur un tableau multidimensionnel de taille modérée. J'ai essayé de timeit utiliser un tableau 42x42, pas inconnu ou même si grand pour les applications bioinformatiques, et j'ai renoncé à attendre une réponse et j'ai juste commencé à taper ma modification dans ce post.

Il semblerait que la seule véritable option consiste alors à initialiser plusieurs listes et à les travailler indépendamment. Si quelqu'un a d'autres suggestions sur la façon de gérer la copie de liste multidimensionnelle, ce serait apprécié.

Comme d'autres l'ont indiqué, il existe des problèmes de performances importants en utilisant le module copy et copy.deepcopy pour les listes multidimensionnelles .

18
Yvette Colomb 13 mai 2019 à 04:09

Commençons par le début et explorons cette question.

Supposons donc que vous ayez deux listes:

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

Et nous devons copier les deux listes, en commençant maintenant par la première liste:

Essayons donc d'abord de définir la variable copy sur notre liste d'origine, list_1:

copy=list_1

Maintenant, si vous pensez copier copié la liste_1, alors vous vous trompez. La fonction id peut nous montrer si deux variables peuvent pointer vers le même objet. Essayons ça:

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

La sortie est:

4329485320
4329485320

Les deux variables sont exactement le même argument. Êtes-vous surpris?

Donc, comme nous savons que python ne stocke rien dans une variable, les variables font simplement référence à l'objet et l'objet stocke la valeur. Ici, l'objet est un list mais nous avons créé deux références à ce même objet par deux noms de variables différents. Cela signifie que les deux variables pointent vers le même objet, juste avec des noms différents.

Quand vous faites copy=list_1, cela fait en fait:

enter image description here

Ici, dans l'image list_1 et copie sont deux noms de variables mais l'objet est le même pour les deux variables qui est list

Donc, si vous essayez de modifier la liste copiée, cela modifiera également la liste d'origine, car la liste n'en est qu'une, vous modifierez cette liste, peu importe ce que vous faites à partir de la liste copiée ou de la liste d'origine:

copy[0]="modify"

print(copy)
print(list_1)

Production:

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

Il a donc modifié la liste d'origine:

Passons maintenant à une méthode pythonique pour copier des listes.

copy_1=list_1[:]

Cette méthode corrige le premier problème rencontré:

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

4338792136
4338791432

Donc, comme nous pouvons voir nos deux listes ayant un identifiant différent et cela signifie que les deux variables pointent vers des objets différents. Donc, ce qui se passe réellement ici, c'est:

enter image description here

Essayons maintenant de modifier la liste et voyons si nous sommes toujours confrontés au problème précédent:

copy_1[0]="modify"

print(list_1)
print(copy_1)

La sortie est:

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

Comme vous pouvez le voir, il n'a modifié que la liste copiée. Cela signifie que cela a fonctionné.

Pensez-vous que nous avons terminé? Essayons de copier notre liste imbriquée.

copy_2=list_2[:]

list_2 doit faire référence à un autre objet qui est une copie de list_2. Allons vérifier:

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

Nous obtenons la sortie:

4330403592 4330403528

Maintenant, nous pouvons supposer que les deux listes pointent sur un objet différent, alors essayons maintenant de le modifier et voyons qu'il donne ce que nous voulons:

copy_2[0][1]="modify"

print(list_2,copy_2)

Cela nous donne la sortie:

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

Cela peut sembler un peu déroutant, car la même méthode que celle utilisée précédemment a fonctionné. Essayons de comprendre cela.

Quand vous faites:

copy_2=list_2[:]

Vous copiez uniquement la liste externe, pas la liste interne. Nous pouvons à nouveau utiliser la fonction id pour vérifier cela.

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

La sortie est:

4329485832
4329485832

Lorsque nous faisons copy_2=list_2[:], cela se produit:

enter image description here

Il crée la copie de la liste mais uniquement la copie de la liste externe, pas la copie de la liste imbriquée, la liste imbriquée est la même pour les deux variables, donc si vous essayez de modifier la liste imbriquée, elle modifiera également la liste d'origine car l'objet de la liste imbriquée est le même pour les deux listes.

Quelle est la solution? La solution est la fonction deepcopy.

from copy import deepcopy
deep=deepcopy(list_2)

Vérifions ceci:

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

4322146056 4322148040

Les deux listes externes ont des ID différents, essayons ceci sur les listes imbriquées internes.

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

La sortie est:

4322145992
4322145800

Comme vous pouvez le voir, les deux ID sont différents, ce qui signifie que nous pouvons supposer que les deux listes imbriquées pointent maintenant sur un objet différent.

Cela signifie que lorsque vous faites deep=deepcopy(list_2) ce qui se passe réellement:

enter image description here

Les deux listes imbriquées pointent sur un objet différent et elles ont maintenant une copie distincte de la liste imbriquée.

Essayons maintenant de modifier la liste imbriquée et de voir si elle a résolu le problème précédent ou non:

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

Il génère:

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

Comme vous pouvez le voir, il n'a pas modifié la liste imbriquée d'origine, il a uniquement modifié la liste copiée.

Si vous aimez ma réponse détaillée, je vous serais reconnaissant de bien vouloir la voter. Si vous avez un doute sur cette réponse, vous pouvez la commenter ci-dessous.

27
Corman 7 févr. 2020 à 22:24

Utilisez thing[:]

>>> a = [1,2]
>>> b = a[:]
>>> a += [3]
>>> a
[1, 2, 3]
>>> b
[1, 2]
>>> 
36
Paul Tarjan 10 avril 2010 à 08:53

L'idiome de Python pour ce faire est newList = oldList[:]

32
erisco 10 avril 2010 à 08:53

Je voulais poster quelque chose d'un peu différent de certaines des autres réponses. Même si ce n'est probablement pas l'option la plus compréhensible ou la plus rapide, elle fournit un aperçu de la façon dont fonctionne la copie en profondeur, tout en étant une autre option alternative pour la copie en profondeur. Peu importe si ma fonction a des bogues, car cela a pour but de montrer un moyen de copier des objets comme les réponses aux questions, mais aussi de l'utiliser comme un point pour expliquer comment fonctionne la deepcopy à sa base.

Au cœur de toute fonction de copie profonde se trouve un moyen de faire une copie superficielle. Comment? Facile. Toute fonction de copie en profondeur ne fait que dupliquer les conteneurs d'objets immuables. Lorsque vous copiez en profondeur une liste imbriquée, vous dupliquez uniquement les listes externes, pas les objets modifiables à l'intérieur des listes. Vous dupliquez uniquement les conteneurs. La même chose fonctionne aussi pour les classes. Lorsque vous copiez en profondeur une classe, vous copiez en profondeur tous ses attributs mutables. Alors, comment? Comment se fait-il que vous n'ayez qu'à copier les conteneurs, comme les listes, les dict, les tuples, les iters, les classes et les instances de classe?

C'est simple. Un objet modifiable ne peut pas vraiment être dupliqué. Il ne peut jamais être modifié, il ne s'agit donc que d'une seule valeur. Cela signifie que vous n'avez jamais à dupliquer des chaînes, des nombres, des bools ou n'importe lequel d'entre eux. Mais comment dupliqueriez-vous les conteneurs? Facile. Vous faites simplement initialiser un nouveau conteneur avec toutes les valeurs. Deepcopy repose sur la récursivité. Il duplique tous les conteneurs, même ceux avec des conteneurs à l'intérieur, jusqu'à ce qu'il ne reste plus de conteneurs. Un conteneur est un objet immuable.

Une fois que vous savez cela, dupliquer complètement un objet sans aucune référence est assez facile. Voici une fonction pour copier en profondeur les types de données de base (ne fonctionnerait pas pour les classes personnalisées mais vous pouvez toujours l'ajouter)

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 copie profonde intégrée de Python est basée sur cet exemple. La seule différence est qu'il prend en charge d'autres types, et prend également en charge les classes d'utilisateurs en dupliquant les attributs dans une nouvelle classe en double, et bloque également la récursion infinie avec une référence à un objet qu'il a déjà vu à l'aide d'une liste de mémos ou d'un dictionnaire. Et c'est vraiment tout pour faire des copies profondes. À la base, faire une copie profonde ne fait que faire des copies superficielles. J'espère que cette réponse ajoute quelque chose à la question.

EXEMPLES

Supposons que vous ayez cette liste: [1, 2, 3] . Les nombres immuables ne peuvent pas être dupliqués, mais l'autre couche le peut. Vous pouvez le dupliquer en utilisant une liste de compréhension: [x pour x dans [1, 2, 3]

Maintenant, imaginez que vous avez cette liste: [[1, 2], [3, 4], [5, 6]] . Cette fois, vous voulez créer une fonction qui utilise la récursivité pour copier en profondeur toutes les couches de la liste. Au lieu de la compréhension de la liste précédente:

[x for x in _list]

Il en utilise un nouveau pour les listes:

[deepcopy_list(x) for x in _list]

Et deepcopy_list ressemble à ceci:

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

Ensuite, vous avez maintenant une fonction qui peut copier en profondeur n'importe quelle liste de chaînes, bools, floast, ints et même listes sur une infinité de couches en utilisant la récursivité. Et voilà, la copie profonde.

TLDR : Deepcopy utilise la récursivité pour dupliquer des objets et renvoie simplement les mêmes objets immuables qu'auparavant, car les objets immuables ne peuvent pas être dupliqués. Cependant, il copie en profondeur les couches les plus internes d'objets mutables jusqu'à ce qu'il atteigne la couche la plus externe d'un objet.

1
Corman 8 sept. 2019 à 02:25

Une légère perspective pratique pour regarder dans la mémoire à travers id et 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 à 19:01

Cela m'étonne que cela n'ait pas encore été mentionné, donc pour être complet ...

Vous pouvez effectuer le déballage de la liste avec "l'opérateur splat": *, qui copiera également les éléments de votre liste.

old_list = [1, 2, 3]

new_list = [*old_list]

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

L'inconvénient évident de cette méthode est qu'elle n'est disponible qu'en Python 3.5+.

Cependant, en termes de calendrier, cela semble mieux fonctionner que les autres méthodes courantes.

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 févr. 2018 à 02:33

Une approche très simple indépendante de la version python manquait dans les réponses déjà données que vous pouvez utiliser la plupart du temps (du moins je le fais):

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

Cependant, si ma_liste contient d'autres conteneurs (par exemple des listes imbriquées), vous devez utiliser la copie profonde comme d'autres l'ont suggéré dans les réponses ci-dessus à partir de la bibliothèque de copie. Par exemple:

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

. Bonus : Si vous ne voulez pas copier les éléments, utilisez (aka copie superficielle):

new_list = my_list[:]

Comprenons la différence entre la solution # 1 et la solution # 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])

Comme vous pouvez le voir, la solution n ° 1 a parfaitement fonctionné lorsque nous n'utilisions pas les listes imbriquées. Voyons ce qui se passera lorsque nous appliquerons la solution # 1 aux listes imbriquées.

>>> 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 à 08:08
new_list = my_list[:]

new_list = my_list Essayez de comprendre cela. Supposons que ma_liste se trouve dans la mémoire du tas à l'emplacement X, c'est-à-dire que ma_liste pointe vers le X. Maintenant, en affectant new_list = my_list, vous laissez new_list pointer vers le X. C'est ce que l'on appelle la copie superficielle.

Maintenant, si vous affectez new_list = my_list[:], vous copiez simplement chaque objet de my_list dans new_list. Ceci est connu sous le nom de copie profonde.

L'autre façon de procéder est la suivante:

  • new_list = list(old_list)
  • import copy new_list = copy.deepcopy(old_list)
3
Ravi Shankar 26 juin 2017 à 21:03

Python 3.6 Timings

Voici les résultats de synchronisation avec Python 3.6.8. Gardez à l'esprit que ces temps sont relatifs et non absolus.

Je suis resté à ne faire que des copies superficielles et j'ai également ajouté de nouvelles méthodes qui n'étaient pas possibles en Python2, telles que list.copy() (le Python3 slice equivalent) et deux formes de déballage des listes (*new_list, = list et 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

Nous pouvons voir que le gagnant de Python2 fonctionne toujours bien, mais ne dépasse pas beaucoup Python3 list.copy(), surtout compte tenu de la lisibilité supérieure de ce dernier.

Le cheval noir est la méthode de déballage et de reconditionnement (b = [*a]), qui est ~ 25% plus rapide que le découpage brut, et plus de deux fois plus rapide que l'autre méthode de déballage (*b, = a).

b = a * 1 s'en sort aussi étonnamment bien.

Notez que ces méthodes ne génèrent pas de résultats équivalents pour toute entrée autre que les listes. Elles fonctionnent toutes pour les objets à découper, quelques-unes fonctionnent pour tout objet itérable, mais seulement {{X0} } fonctionne pour les objets Python plus généraux.


Voici le code de test pour les parties intéressées (Modèle d'ici ):

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 mai 2019 à 02:54

Il existe déjà de nombreuses réponses qui vous indiquent comment faire une copie correcte, mais aucune d'entre elles ne dit pourquoi votre copie originale a échoué.

Python ne stocke pas de valeurs dans des variables; il lie les noms aux objets. Votre affectation d'origine a pris l'objet référencé par my_list et l'a lié à new_list également. Peu importe le nom que vous utilisez, il n'y a toujours qu'une seule liste, donc les modifications apportées lorsque vous vous y référez en tant que my_list persisteront lorsque vous vous y référerez en tant que new_list. Chacune des autres réponses à cette question vous donne différentes façons de créer un nouvel objet à lier à new_list.

Chaque élément d'une liste agit comme un nom, en ce sens que chaque élément se lie non exclusivement à un objet. Une copie superficielle crée une nouvelle liste dont les éléments se lient aux mêmes objets qu'auparavant.

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]

Pour aller plus loin, copiez votre liste, copiez chaque objet auquel votre liste fait référence et liez ces copies d'élément à une nouvelle liste.

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

Ce n'est pas encore une copie complète, car chaque élément d'une liste peut faire référence à d'autres objets, tout comme la liste est liée à ses éléments. Pour copier récursivement chaque élément de la liste, puis chaque autre objet référencé par chaque élément, etc.: effectuez une copie complète.

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

Voir la documentation pour plus d'informations sur les cas d'angle lors de la copie.

52
jack 19 janv. 2018 à 16:00

On m'a informé que Python 3.3+ ajoute la méthode list.copy(), qui devrait être aussi rapide que le découpage:

newlist = old_list.copy()

145
River 5 avril 2017 à 01:13

N'oubliez pas qu'en Python lorsque vous faites:

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

List2 ne stocke pas la liste réelle, mais une référence à list1. Ainsi, lorsque vous faites quoi que ce soit pour list1, list2 change également. utilisez le module de copie (pas par défaut, téléchargez sur pip) pour faire une copie originale de la liste (copy.copy() pour les listes simples, copy.deepcopy() pour les listes imbriquées). Cela fait une copie qui ne change pas avec la première liste.

0
Dr. Hippo 22 févr. 2020 à 12:44