Frage Wie klonen oder kopieren Sie eine Liste?


Welche Möglichkeiten gibt es, eine Liste in Python zu klonen oder zu kopieren?

Verwenden new_list = my_list ändert sich dann new_list jedes Mal my_list Änderungen.
Warum ist das?


1690
2018-04-10 08:49


Ursprung


Antworten:


Mit new_list = my_list, Sie haben eigentlich nicht zwei Listen. Die Zuweisung kopiert nur den Verweis auf die Liste, nicht die eigentliche Liste, also beides new_list und my_list beziehen Sie sich nach der Zuweisung auf dieselbe Liste.

Um die Liste tatsächlich zu kopieren, haben Sie verschiedene Möglichkeiten:

  • Sie können das eingebaute verwenden list.copy() Methode (verfügbar seit Python 3.3):

    new_list = old_list.copy()
    
  • Sie können es in Scheiben schneiden:

    new_list = old_list[:]
    

    Alex Martelli Meinung (zumindest zurück im Jahr 2007) darüber ist, dass Es ist eine seltsame Syntax und es macht keinen Sinn es jemals zu benutzen. ;) (Seiner Meinung nach ist der nächste besser lesbar).

  • Sie können das eingebaute verwenden list() Funktion:

    new_list = list(old_list)
    
  • Sie können generisch verwenden copy.copy():

    import copy
    new_list = copy.copy(old_list)
    

    Das ist ein bisschen langsamer als list() weil es den Datentyp von herausfinden muss old_list zuerst.

  • Wenn die Liste Objekte enthält und Sie diese auch kopieren möchten, verwenden Sie generisch copy.deepcopy():

    import copy
    new_list = copy.deepcopy(old_list)
    

    Offensichtlich die langsamste und am meisten speicherbedürftige Methode, aber manchmal unvermeidbar.

Beispiel:

import copy

class Foo(object):
    def __init__(self, val):
         self.val = val

    def __repr__(self):
        return str(self.val)

foo = Foo(1)

a = ['foo', foo]
b = a.copy()
c = a[:]
d = list(a)
e = copy.copy(a)
f = copy.deepcopy(a)

# edit orignal list and instance 
a.append('baz')
foo.val = 5

print('original: %r\n list.copy(): %r\n slice: %r\n list(): %r\n copy: %r\n deepcopy: %r'
      % (a, b, c, d, e, f))

Ergebnis:

original: ['foo', 5, 'baz']
list.copy(): ['foo', 5]
slice: ['foo', 5]
list(): ['foo', 5]
copy: ['foo', 5]
deepcopy: ['foo', 1]

2315
2018-04-10 08:55



Felix hat bereits eine ausgezeichnete Antwort gegeben, aber ich dachte, ich würde einen Geschwindigkeitsvergleich der verschiedenen Methoden machen:

  1. 10,59 sec (105.9us / itn) - copy.deepcopy(old_list)
  2. 10.16 sec (101.6us / itn) - reiner Python Copy() Methoden kopieren Klassen mit Deepcopy
  3. 1.488 sec (14.88us / itn) - reine Python Copy() Methode kopiert keine Klassen (nur Dicts / Listen / Tupel)
  4. 0,325 sec (3,25 us / itn) - for item in old_list: new_list.append(item)
  5. 0,217 sec (2,17us / itn) - [i for i in old_list] (ein Listenverständnis)
  6. 0,186 sec (1,86us / itn) - copy.copy(old_list)
  7. 0,075 s (0,75 us / itn) - list(old_list)
  8. 0,053 sec (0,53 us / itn) - new_list = []; new_list.extend(old_list)
  9. 0,039 sec (0,39us / itn) - old_list[:] (Slicing auflisten)

Das schnellste ist das Listen-Slicing. Aber sei dir dessen bewusst copy.copy(), list[:] und list(list), nicht wie copy.deepcopy() und die Python-Version kopiert keine Listen, Wörterbücher und Klasseninstanzen in die Liste. Wenn sich also die Originale ändern, werden sie auch in der kopierten Liste geändert und umgekehrt.

(Hier ist das Skript, wenn jemand interessiert ist oder irgendwelche Probleme aufwerfen will :)

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

BEARBEITEN: Hinzugefügt neue Stil, alten Stil Klassen und dicts zu den Benchmarks, und machte die Python-Version viel schneller und fügte einige weitere Methoden einschließlich Listenausdrücke und extend().


447
2018-04-10 10:16



Ich habe wurde gesagt das Python 3.3+ fügt hinzu list.copy() Methode, die so schnell wie Slicing sein sollte:

newlist = old_list.copy()


116
2017-07-23 12:32



Welche Möglichkeiten gibt es, eine Liste in Python zu klonen oder zu kopieren?

In Python 3 kann eine flache Kopie erstellt werden mit:

a_copy = a_list.copy()

In Python 2 und 3 erhalten Sie eine flache Kopie mit einem vollständigen Ausschnitt des Originals:

a_copy = a_list[:]

Erläuterung

Es gibt zwei semantische Möglichkeiten, eine Liste zu kopieren. Eine flache Kopie erstellt eine neue Liste der gleichen Objekte, eine tiefe Kopie erstellt eine neue Liste mit neuen äquivalenten Objekten.

Flache Liste kopieren

Bei einer flachen Kopie wird nur die Liste selbst kopiert, bei der es sich um einen Container mit Verweisen auf die Objekte in der Liste handelt. Wenn die enthaltenen Objekte veränderbar sind und eines geändert wird, wird sich die Änderung in beiden Listen widerspiegeln.

Es gibt verschiedene Möglichkeiten, dies in Python 2 und 3 zu tun. Die Python 2-Wege funktionieren auch in Python 3.

Python 2

In Python 2 besteht die idiomatische Methode, eine flache Kopie einer Liste mit einem vollständigen Ausschnitt des Originals zu erstellen:

a_copy = a_list[:]

Sie können dasselbe auch erreichen, indem Sie die Liste über den Listenkonstruktor übergeben.

a_copy = list(a_list)

aber die Verwendung des Konstruktors ist weniger effizient:

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

Python 3

In Python 3 erhalten Listen die list.copy Methode:

a_copy = a_list.copy()

In 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

Einen anderen Zeiger machen nicht eine Kopie machen

Die Verwendung von new_list = my_list ändert dann new_list jedes Mal, wenn sich my_list ändert. Warum ist das?

my_list ist nur ein Name, der auf die tatsächliche Liste im Speicher verweist. Wenn du sagst new_list = my_list Sie erstellen keine Kopie, sondern fügen einfach einen anderen Namen hinzu, der auf die ursprüngliche Liste im Speicher verweist. Wir können ähnliche Probleme haben, wenn wir Kopien von Listen erstellen.

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

Die Liste ist nur ein Array von Zeigern auf den Inhalt, so dass eine flache Kopie nur die Zeiger kopiert, und so haben Sie zwei verschiedene Listen, aber sie haben den gleichen Inhalt. Um Kopien des Inhalts zu erstellen, benötigen Sie eine tiefe Kopie.

Tiefe Kopien

Ein ... machen tiefe Kopie einer Liste, in Python 2 oder 3, verwenden deepcopy in dem copy Modul:

import copy
a_deep_copy = copy.deepcopy(a_list)

Um zu zeigen, wie wir neue Unterlisten erstellen können:

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

Und so sehen wir, dass die tief kopierte Liste eine völlig andere Liste als das Original ist. Sie könnten Ihre eigene Funktion rollen - aber nicht. Sie werden wahrscheinlich Fehler erzeugen, die Sie sonst nicht hätten, wenn Sie die Deepcopy-Funktion der Standardbibliothek verwenden.

Benutze es nicht eval

Sie können dies als eine Möglichkeit zum Deepcopy sehen, aber tun Sie es nicht:

problematic_deep_copy = eval(repr(a_list))
  1. Es ist gefährlich, besonders wenn Sie etwas aus einer Quelle bewerten, der Sie nicht vertrauen.
  2. Es ist nicht zuverlässig, wenn ein Unterelement, das Sie kopieren, nicht über eine Darstellung verfügt, die für die Reproduktion eines äquivalenten Elements ausgewertet werden kann.
  3. Es ist auch weniger leistungsfähig.

In 64-Bit-Python 2.7:

>>> 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

auf 64 Bit Python 3.5:

>>> 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

87
2017-10-25 12:13



Es gibt bereits viele Antworten, die Ihnen sagen, wie man eine richtige Kopie erstellt, aber keiner von ihnen sagt, warum Ihre ursprüngliche 'Kopie' fehlgeschlagen ist.

Python speichert keine Werte in Variablen. es bindet Namen an Objekte. Ihre ursprüngliche Aufgabe hat das Objekt übernommen, auf das verwiesen wird my_list und gebunden es an new_list auch. Egal welchen Namen Sie verwenden, es gibt immer noch nur eine Liste, also werden Änderungen gemacht, wenn Sie darauf verweisen my_list wird fortfahren, wenn man es als new_list. Jede der anderen Antworten auf diese Frage gibt Ihnen verschiedene Möglichkeiten, ein neues Objekt zu erstellen, an das gebunden werden soll new_list.

Jedes Element einer Liste verhält sich wie ein Name, indem jedes Element nicht ausschließlich an ein Objekt bindet. Eine flache Kopie erstellt eine neue Liste, deren Elemente an die gleichen Objekte wie zuvor binden.

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]

Kopieren Sie jedes Objekt, auf das Ihre Liste verweist, um diese Liste zu kopieren, und binden Sie diese Elementkopien an eine neue Liste.

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

Dies ist noch keine tiefe Kopie, weil jedes Element einer Liste sich auf andere Objekte beziehen kann, genauso wie die Liste an ihre Elemente gebunden ist. Um jedes Element in der Liste und dann jedes andere Objekt rekursiv zu kopieren, auf das von jedem Element verwiesen wird, usw., führen Sie eine tiefe Kopie durch.

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

Sehen die Dokumentation für weitere Informationen über Eckfälle beim Kopieren.


42
2017-11-23 16:45



new_list = list(old_list)


30
2018-04-10 09:03



Benutzen thing[:]

>>> a = [1,2]
>>> b = a[:]
>>> a += [3]
>>> a
[1, 2, 3]
>>> b
[1, 2]
>>> 

27
2018-04-10 08:53



Pythons Redewendung dafür ist newList = oldList[:]


26
2018-04-10 08:53