Wie klone oder kopiere ich eine Liste?

2547

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

Während der Verwendung new_list = my_listwerden Änderungen an new_listÄnderungen my_listjedes Mal vorgenommen. Warum ist das?

aF.
quelle

Antworten:

3328

Mit new_list = my_listhaben Sie eigentlich nicht zwei Listen. Die Zuweisung kopiert nur den Verweis auf die Liste, nicht die tatsächliche Liste, also beide new_listund my_listverweist nach der Zuweisung auf dieselbe Liste.

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

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

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

    new_list = old_list[:]

    Alex Martellis Meinung (zumindest 2007 ) ist, dass es sich um eine seltsame Syntax handelt und es keinen Sinn macht, sie jemals zu verwenden . ;) (Seiner Meinung nach ist der nächste besser lesbar).

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

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

    import copy
    new_list = copy.copy(old_list)
    

    Dies ist etwas langsamer als list()weil es zuerst den Datentyp von herausfinden muss old_list.

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

    import copy
    new_list = copy.deepcopy(old_list)
    

    Offensichtlich die langsamste und speicherintensivste Methode, aber manchmal unvermeidlich.

Beispiel:

import copy

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

    def __repr__(self):
        return 'Foo({!r})'.format(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\nlist.copy(): %r\nslice: %r\nlist(): %r\ncopy: %r\ndeepcopy: %r'
      % (a, b, c, d, e, f))

Ergebnis:

original: ['foo', Foo(5), 'baz']
list.copy(): ['foo', Foo(5)]
slice: ['foo', Foo(5)]
list(): ['foo', Foo(5)]
copy: ['foo', Foo(5)]
deepcopy: ['foo', Foo(1)]
Felix Kling
quelle
7
Wenn ich mich nicht irre: newlist = [*mylist]ist auch eine Möglichkeit in Python 3. newlist = list(mylist)Vielleicht ist es aber klarer.
Stéphane
9
Eine andere Möglichkeit ist new_list = old_list * 1
aris
4
Welche dieser Methoden sind flache Kopien und welche sind tiefe Kopien?
Eswar
4
@Eswar: Alle bis auf den letzten machen eine flache Kopie
Felix Kling
3
@Eswar es ist eine flache Kopie.
juanpa.arrivillaga
602

Felix gab bereits eine ausgezeichnete Antwort, aber ich dachte, ich würde einen Geschwindigkeitsvergleich der verschiedenen Methoden durchführen:

  1. 10,59 s (105,9 us / itn) - copy.deepcopy(old_list)
  2. 10,16 Sek. (101,6us / itn) - reine Python- Copy()Methode zum Kopieren von Klassen mit Deepcopy
  3. 1.488 Sek. (14.88us / itn) - reine Python- Copy()Methode, die keine Klassen kopiert (nur Dikte / Listen / Tupel)
  4. 0,325 s (3,25 us / itn) - for item in old_list: new_list.append(item)
  5. 0,217 Sek. (2,17us / itn) - [i for i in old_list](ein Listenverständnis )
  6. 0,186 s (1,86 us / itn) - copy.copy(old_list)
  7. 0,075 s (0,75 us / itn) - list(old_list)
  8. 0,053 s (0,53 us / itn) - new_list = []; new_list.extend(old_list)
  9. 0,039 Sek. (0,39 us / itn) - old_list[:]( Listenaufteilung )

Das schnellste ist also das Aufteilen von Listen. Aber beachten Sie, dass copy.copy(), list[:]und list(list), im Gegensatz zu copy.deepcopy()und die Python - Version nicht kopiert keine Listen, Wörterbücher und Klasseninstanzen in der Liste, so dass , wenn die Originale zu ändern, werden sie in der kopierten Liste ändern zu und umgekehrt.

(Hier ist das Skript, wenn jemand interessiert ist oder Probleme ansprechen möchte :)

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
Kryo
quelle
9
Da Sie ein Benchmarking durchführen, kann es hilfreich sein, einen Referenzpunkt anzugeben. Sind diese Zahlen 2017 mit Python 3.6 mit vollständig kompiliertem Code noch korrekt? Ich stelle fest, dass die Antwort unten ( stackoverflow.com/a/17810305/26219 ) diese Antwort bereits in Frage stellt.
Mark Edington
4
Verwenden Sie das timeitModul. Außerdem kann man aus solchen willkürlichen Mikro-Benchmarks nicht viel schließen.
Corey Goldberg
3
Wenn Sie eine neue Option für 3.5+ einfügen möchten, [*old_list]sollte dies in etwa der Option entsprechen. list(old_list)Da es sich jedoch um eine Syntax handelt, die keine allgemeinen Funktionsaufrufpfade sind, wird ein wenig Laufzeit eingespart (und im Gegensatz zu old_list[:]der Option , die keine Konvertierung eingibt). [*old_list]funktioniert auf jedem iterable und erzeugt a list).
ShadowRanger
3
@CoreyGoldberg für einen etwas weniger willkürlichen Mikro-Benchmark (verwendet timeit50 m
River
1
@ShadowRanger [*old_list]scheint tatsächlich fast jede andere Methode zu übertreffen. (siehe meine Antwort in früheren Kommentaren verlinkt)
Fluss
125

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 können Sie eine flache Kopie mit einem vollständigen Ausschnitt des Originals erhalten:

a_copy = a_list[:]

Erläuterung

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

Flache Listenkopie

Eine flache Kopie kopiert nur die Liste selbst, bei der es sich um einen Container mit Verweisen auf die Objekte in der Liste handelt. Wenn die darin enthaltenen Objekte veränderlich sind und eines geändert wird, wird die Änderung in beiden Listen wiedergegeben.

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

Python 2

In Python 2 besteht die idiomatische Art, eine flache Kopie einer Liste zu erstellen, darin, ein vollständiges Stück des Originals zu erstellen:

a_copy = a_list[:]

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

a_copy = list(a_list)

Die Verwendung des Konstruktors ist jedoch 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 folgende list.copyMethode:

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

Wenn Sie einen anderen Zeiger erstellen, wird keine Kopie erstellt

Wenn Sie new_list = my_list verwenden, wird new_list jedes Mal geändert, wenn sich my_list ändert. Warum ist das?

my_listist nur ein Name, der auf die tatsächliche Liste im Speicher verweist. Wenn Sie sagen, dass new_list = my_listSie keine Kopie erstellen, fügen Sie einfach einen anderen Namen hinzu, der auf diese 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. Eine flache Kopie kopiert also nur die Zeiger. Sie haben also zwei verschiedene Listen, aber sie haben den gleichen Inhalt. Um Kopien des Inhalts zu erstellen, benötigen Sie eine tiefe Kopie.

Tiefe Kopien

Verwenden Sie deepcopyim copyModul Folgendes, um eine tiefe Kopie einer Liste in Python 2 oder 3 zu erstellen :

import copy
a_deep_copy = copy.deepcopy(a_list)

Um zu demonstrieren, wie wir auf diese Weise 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'], [], []]

Wir sehen also, dass die tief kopierte Liste eine völlig andere Liste ist als das Original. Sie könnten Ihre eigene Funktion rollen - aber nicht. Mit der Deepcopy-Funktion der Standardbibliothek können Sie wahrscheinlich Fehler verursachen, die Sie sonst nicht hätten.

Nicht benutzen eval

Sie können dies als einen Weg zur Deepcopy sehen, aber tun Sie es nicht:

problematic_deep_copy = eval(repr(a_list))
  1. Dies ist gefährlich, insbesondere wenn Sie etwas aus einer Quelle bewerten, der Sie nicht vertrauen.
  2. Es ist nicht zuverlässig, wenn ein Unterelement, das Sie kopieren, keine Darstellung hat, die ausgewertet werden kann, um ein äquivalentes Element zu reproduzieren.
  3. Es ist auch weniger performant.

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
Aaron Hall
quelle
1
Sie benötigen keine Deepcopy, wenn die Liste 2D ist. Wenn es sich um eine Liste von Listen handelt und diese Listen keine Listen enthalten, können Sie eine for-Schleife verwenden. Zur Zeit benutze ich list_copy=[] for item in list: list_copy.append(copy(item))und es ist viel schneller.
John Locke
53

Es gibt bereits viele Antworten, die Ihnen sagen, wie Sie eine ordnungsgemäße Kopie erstellen können, aber keine davon gibt an, 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, auf das von verwiesen wird, ebenfalls my_listgebunden new_list. Unabhängig davon, welchen Namen Sie verwenden, gibt es immer noch nur eine Liste. Änderungen, die beim Verweisen auf diesen Namen vorgenommen my_listwerden, bleiben also bestehen, wenn Sie auf ihn verweisen new_list. Jede der anderen Antworten auf diese Frage bietet Ihnen verschiedene Möglichkeiten, ein neues Objekt zum Binden zu erstellen new_list.

Jedes Element einer Liste verhält sich wie ein Name, indem jedes Element nicht ausschließlich an ein Objekt gebunden wird. Eine flache Kopie erstellt eine neue Liste, deren Elemente an dieselben Objekte wie zuvor gebunden sind.

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]

Um Ihre Listenkopie noch einen Schritt weiter zu führen, kopieren Sie jedes Objekt, auf das sich Ihre Liste bezieht, 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, da jedes Element einer Liste auf andere Objekte verweisen kann, genau wie die Liste an ihre Elemente gebunden ist. So rekursiv kopieren Sie jedes Element in der Liste und dann jedes andere Objekt, auf das jedes Element verweist, usw. Führen Sie eine Tiefenkopie durch.

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

Weitere Informationen zu Eckfällen beim Kopieren finden Sie in der Dokumentation .

Jack
quelle
37

Verwenden thing[:]

>>> a = [1,2]
>>> b = a[:]
>>> a += [3]
>>> a
[1, 2, 3]
>>> b
[1, 2]
>>> 
Paul Tarjan
quelle
34

Beginnen wir von vorne und untersuchen diese Frage.

Nehmen wir also an, Sie haben zwei Listen:

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

Und wir müssen beide Listen kopieren, beginnend mit der ersten Liste:

Versuchen wir also zunächst, die Variable copyauf unsere ursprüngliche Liste zu setzen list_1:

copy=list_1

Wenn Sie jetzt denken, kopieren Sie die Liste_1, dann liegen Sie falsch. Die idFunktion kann uns zeigen, ob zwei Variablen auf dasselbe Objekt zeigen können. Lass uns das versuchen:

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

Die Ausgabe ist:

4329485320
4329485320

Beide Variablen sind genau das gleiche Argument. Bist du überrascht?

Da wir wissen, dass Python nichts in einer Variablen speichert, verweisen Variablen nur auf das Objekt und das Objekt speichert den Wert. Hier ist das Objekt ein, listaber wir haben zwei Verweise auf dasselbe Objekt durch zwei verschiedene Variablennamen erstellt. Dies bedeutet, dass beide Variablen auf dasselbe Objekt zeigen, nur mit unterschiedlichen Namen.

Wenn Sie dies tun copy=list_1, geschieht tatsächlich Folgendes:

Geben Sie hier die Bildbeschreibung ein

Hier in der Bildliste_1 und Kopie sind zwei Variablennamen, aber das Objekt ist für beide Variablen gleich list

Wenn Sie also versuchen, die kopierte Liste zu ändern, wird auch die ursprüngliche Liste geändert, da die Liste nur eine enthält. Sie ändern diese Liste unabhängig davon, ob Sie sie aus der kopierten Liste oder aus der ursprünglichen Liste ausführen:

copy[0]="modify"

print(copy)
print(list_1)

Ausgabe:

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

Also hat es die ursprüngliche Liste geändert:

Kommen wir nun zu einer pythonischen Methode zum Kopieren von Listen.

copy_1=list_1[:]

Diese Methode behebt das erste Problem, das wir hatten:

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

4338792136
4338791432

Wie wir sehen können, haben unsere beiden Listen unterschiedliche IDs und es bedeutet, dass beide Variablen auf unterschiedliche Objekte zeigen. Was hier also tatsächlich vor sich geht, ist:

Geben Sie hier die Bildbeschreibung ein

Versuchen wir nun, die Liste zu ändern und zu prüfen, ob das vorherige Problem weiterhin besteht:

copy_1[0]="modify"

print(list_1)
print(copy_1)

Die Ausgabe ist:

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

Wie Sie sehen, wurde nur die kopierte Liste geändert. Das heißt, es hat funktioniert.

Glaubst du, wir sind fertig? Versuchen wir, unsere verschachtelte Liste zu kopieren.

copy_2=list_2[:]

list_2sollte auf ein anderes Objekt verweisen, von dem eine Kopie ist list_2. Lass uns nachsehen:

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

Wir bekommen die Ausgabe:

4330403592 4330403528

Jetzt können wir davon ausgehen, dass beide Listen auf unterschiedliche Objekte verweisen. Versuchen wir nun, sie zu ändern, und sehen, dass sie das geben, was wir wollen:

copy_2[0][1]="modify"

print(list_2,copy_2)

Dies gibt uns die Ausgabe:

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

Dies mag etwas verwirrend erscheinen, da dieselbe Methode, die wir zuvor verwendet haben, funktioniert hat. Versuchen wir das zu verstehen.

Wenn Sie das tun:

copy_2=list_2[:]

Sie kopieren nur die äußere Liste, nicht die innere Liste. Wir können die idFunktion noch einmal verwenden, um dies zu überprüfen.

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

Die Ausgabe ist:

4329485832
4329485832

Wenn wir das tun copy_2=list_2[:], passiert Folgendes:

Geben Sie hier die Bildbeschreibung ein

Es wird die Kopie der Liste erstellt, aber nur die Kopie der äußeren Liste, nicht die Kopie der verschachtelten Liste. Die verschachtelte Liste ist für beide Variablen gleich. Wenn Sie also versuchen, die verschachtelte Liste zu ändern, wird auch die ursprüngliche Liste geändert, da das verschachtelte Listenobjekt identisch ist für beide Listen.

Was ist die Lösung? Die Lösung ist die deepcopyFunktion.

from copy import deepcopy
deep=deepcopy(list_2)

Lassen Sie uns dies überprüfen:

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

4322146056 4322148040

Beide äußeren Listen haben unterschiedliche IDs. Versuchen wir dies bei den inneren verschachtelten Listen.

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

Die Ausgabe ist:

4322145992
4322145800

Wie Sie sehen können, sind beide IDs unterschiedlich, was bedeutet, dass wir davon ausgehen können, dass beide verschachtelten Listen jetzt auf unterschiedliche Objekte zeigen.

Das heißt, wenn Sie tun, deep=deepcopy(list_2)was tatsächlich passiert:

Geben Sie hier die Bildbeschreibung ein

Beide verschachtelten Listen zeigen auf unterschiedliche Objekte und haben jetzt eine separate Kopie der verschachtelten Liste.

Versuchen wir nun, die verschachtelte Liste zu ändern und festzustellen, ob das vorherige Problem behoben wurde oder nicht:

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

Es gibt aus:

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

Wie Sie sehen, wurde die ursprünglich verschachtelte Liste nicht geändert, sondern nur die kopierte Liste.

Aaditya Ura
quelle
33

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

erisco
quelle
33

Python 3.6 Timings

Hier sind die Timing-Ergebnisse mit Python 3.6.8. Denken Sie daran, dass diese Zeiten relativ zueinander und nicht absolut sind.

Ich habe mich daran gehalten, nur flache Kopien zu erstellen, und einige neue Methoden hinzugefügt, die in Python2 nicht möglich waren, wie list.copy()(das Python3- Slice-Äquivalent ) und zwei Formen des Entpackens von Listen ( *new_list, = listund 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

Wir können sehen, dass der Python2-Gewinner immer noch gut abschneidet, Python3 jedoch nicht list.copy()wesentlich übertrifft , insbesondere angesichts der überlegenen Lesbarkeit des letzteren.

Das dunkle Pferd ist die Auspack- und Umpackmethode ( b = [*a]), die ~ 25% schneller als das rohe Schneiden und mehr als doppelt so schnell wie die andere Auspackmethode ( *b, = a) ist.

b = a * 1 macht sich auch überraschend gut.

Beachten Sie, dass diese Methoden tun nicht gleichwertige Ergebnisse für jede Eingabe andere als Listen ausgegeben. Sie funktionieren alle für schneidbare Objekte, einige für iterierbare Objekte, aber nur copy.copy()für allgemeinere Python-Objekte.


Hier ist der Testcode für Interessenten ( Vorlage von hier ):

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))
Fluss
quelle
1
Kann noch eine ähnliche Geschichte auf 3.8 bestätigen b=[*a]- der eine offensichtliche Weg, dies zu tun;).
SuperShoot
19

Alle anderen Mitwirkenden gaben großartige Antworten, die funktionieren, wenn Sie eine eindimensionale (abgestufte) Liste haben. Die bisher genannten Methoden copy.deepcopy()funktionieren jedoch nur zum Klonen / Kopieren einer Liste und lassen sie nicht auf die verschachtelten listObjekte verweisen, wenn Sie sich befinden Arbeiten mit mehrdimensionalen, verschachtelten Listen (Liste der Listen). Während Felix Kling in seiner Antwort darauf verweist, gibt es ein bisschen mehr zu dem Problem und möglicherweise eine Problemumgehung mit integrierten Funktionen, die sich als schnellere Alternative zu erweisen könnten deepcopy.

Während new_list = old_list[:], copy.copy(old_list)'und für Py3k old_list.copy()Arbeit für Einzel nivelliert Listen, sie werden wieder an den zu zeigen listinnerhalb der verschachtelten Objekte old_listund die new_list, und Änderungen an einem der listObjekte in der anderen verewigt.

Bearbeiten: Neue Informationen ans Licht gebracht

Wie sowohl von Aaron Hall als auch von PM 2Ring hervorgehoben wurde , eval()ist die Verwendung nicht nur eine schlechte Idee, sondern auch viel langsamer als copy.deepcopy().

Dies bedeutet, dass für mehrdimensionale Listen die einzige Option ist copy.deepcopy(). Abgesehen davon ist dies keine Option, da die Leistung weit nach Süden geht, wenn Sie versuchen, sie auf einem mittelgroßen mehrdimensionalen Array zu verwenden. Ich habe versucht, timeitein 42x42-Array zu verwenden, das für Bioinformatik-Anwendungen nicht ungewöhnlich oder sogar so groß ist, und ich habe es aufgegeben, auf eine Antwort zu warten, und habe gerade angefangen, meine Bearbeitung für diesen Beitrag einzugeben.

Es scheint, dass die einzige echte Option darin besteht, mehrere Listen zu initialisieren und unabhängig voneinander zu bearbeiten. Wenn jemand andere Vorschläge zum Umgang mit mehrdimensionalem Listenkopieren hat, wäre er dankbar.

Wie bereits erwähnt, treten bei Verwendung des copyModuls und copy.deepcopy bei mehrdimensionalen Listen erhebliche Leistungsprobleme auf .

AMR
quelle
5
Dies funktioniert nicht immer, da nicht garantiert werden kann, dass die von zurückgegebene Zeichenfolge repr()ausreicht, um das Objekt neu zu erstellen. Auch eval()ist ein Werkzeug der letzten Instanz; siehe Eval ist wirklich gefährlich von SO-Veteran Ned Batchelder für Details. Wenn Sie sich für die Verwendung einsetzen eval(), sollten Sie wirklich erwähnen, dass dies gefährlich sein kann.
PM 2Ring
1
Gutes Argument. Obwohl ich denke, dass Batchelder der Meinung ist, dass die eval()Funktion in Python im Allgemeinen ein Risiko darstellt. Es ist nicht so sehr, ob Sie die Funktion im Code verwenden oder nicht, sondern dass es sich um eine Sicherheitslücke in Python an und für sich handelt. Mein Beispiel ist die Verwendung nicht mit einer Funktion , den Eingang empfängt von input(), sys.agrvoder sogar eine Textdatei. Es geht eher darum, eine leere mehrdimensionale Liste einmal zu initialisieren und sie dann einfach in eine Schleife zu kopieren, anstatt sie bei jeder Iteration der Schleife neu zu initialisieren.
AMR
1
Wie @AaronHall hervorgehoben hat, gibt es wahrscheinlich ein erhebliches Leistungsproblem bei der Verwendung. Abgesehen davon new_list = eval(repr(old_list)), dass es eine schlechte Idee ist, ist es wahrscheinlich auch viel zu langsam, um zu arbeiten.
AMR
12

Es überrascht mich, dass dies noch nicht erwähnt wurde, der Vollständigkeit halber ...

Sie können das Entpacken von Listen mit dem "splat operator": durchführen *, der auch Elemente Ihrer Liste kopiert.

old_list = [1, 2, 3]

new_list = [*old_list]

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

Der offensichtliche Nachteil dieser Methode ist, dass sie nur in Python 3.5+ verfügbar ist.

In Bezug auf das Timing scheint dies jedoch besser zu funktionieren als andere gängige Methoden.

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)
SCB
quelle
1
Wie verhält sich diese Methode beim Ändern von Kopien?
not2qubit
2
@ not2qubit meinst du das Anhängen oder Bearbeiten von Elementen der neuen Liste. In dem Beispiel old_listund new_listsind zwei verschiedene Listen, die Bearbeitung wird man nicht die andere ändern (es sei denn , es ist direkt auf die Elementen selbst (wie Liste der Liste) mutiert, nichts dieser Methoden sind tiefe Kopien).
SCB
7

Ein sehr einfacher Ansatz unabhängig von der Python-Version fehlte in bereits gegebenen Antworten, die Sie die meiste Zeit verwenden können (zumindest ich):

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

Wenn my_list jedoch andere Container enthält (z. B. verschachtelte Listen), müssen Sie deepcopy verwenden, wie in den obigen Antworten aus der Kopierbibliothek vorgeschlagen. Zum Beispiel:

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

. Bonus : Wenn Sie keine Elemente kopieren möchten, verwenden Sie (auch bekannt als flache Kopie):

new_list = my_list[:]

Lassen Sie uns den Unterschied zwischen Lösung 1 und Lösung 2 verstehen

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

Wie Sie sehen, hat Lösung 1 perfekt funktioniert, als wir die verschachtelten Listen nicht verwendet haben. Lassen Sie uns überprüfen, was passieren wird, wenn wir Lösung 1 auf verschachtelte Listen anwenden.

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

Beachten Sie, dass es einige Fälle gibt, in denen Sie, wenn Sie Ihre eigene benutzerdefinierte Klasse definiert haben und die Attribute beibehalten möchten, die Alternativen verwenden sollten copy.copy()oder copy.deepcopy()nicht, z. B. in 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))

Ausgänge:

lst: original, name: custom list
lst: slicecopy, name: NA
lst: lstcopy, name: NA
lst: copycopy, name: custom list
lst: deepcopy, name: custom list
Chris_Rands
quelle
4
new_list = my_list[:]

new_list = my_list Versuchen Sie das zu verstehen. Angenommen, my_list befindet sich im Heapspeicher an Position X, dh my_list zeigt auf das X. new_list = my_listIndem Sie jetzt zuweisen, dass new_list auf das X zeigt. Dies wird als flache Kopie bezeichnet.

Wenn Sie jetzt zuweisen, new_list = my_list[:]kopieren Sie einfach jedes Objekt von my_list nach new_list. Dies ist als Deep Copy bekannt.

Die andere Möglichkeit, dies zu tun, ist:

  • new_list = list(old_list)
  • import copy new_list = copy.deepcopy(old_list)
Ravi Shankar
quelle
2

Ich wollte etwas anderes posten als einige der anderen Antworten. Auch wenn dies höchstwahrscheinlich nicht die verständlichste oder schnellste Option ist, bietet es einen Einblick in die Funktionsweise von Deep Copy und ist eine weitere alternative Option für Deep Copy. Es spielt keine Rolle, ob meine Funktion Fehler aufweist, da der Zweck darin besteht, einen Weg aufzuzeigen, um Objekte wie die Antworten auf Fragen zu kopieren, aber dies auch als Punkt zu verwenden, um zu erklären, wie Deepcopy im Kern funktioniert.

Im Mittelpunkt jeder Deep Copy-Funktion steht die Möglichkeit, eine flache Kopie zu erstellen. Wie? Einfach. Jede Deep Copy-Funktion dupliziert nur die Container unveränderlicher Objekte. Wenn Sie eine verschachtelte Liste tief kopieren, duplizieren Sie nur die äußeren Listen, nicht die veränderlichen Objekte in den Listen. Sie duplizieren nur die Container. Das gleiche gilt auch für Klassen. Wenn Sie eine Klasse vertiefen, kopieren Sie alle ihre veränderlichen Attribute. Also, wie? Wie kommt es, dass Sie nur die Container wie Listen, Diktate, Tupel, Iter, Klassen und Klasseninstanzen kopieren müssen?

Es ist einfach. Ein veränderliches Objekt kann nicht wirklich dupliziert werden. Es kann niemals geändert werden, es ist also nur ein einziger Wert. Das bedeutet, dass Sie niemals Zeichenfolgen, Zahlen, Bools oder ähnliches duplizieren müssen. Aber wie würden Sie die Container duplizieren? Einfach. Sie initialisieren einfach einen neuen Container mit allen Werten. Deepcopy beruht auf Rekursion. Es dupliziert alle Container, auch solche mit Containern, bis keine Container mehr übrig sind. Ein Container ist ein unveränderliches Objekt.

Sobald Sie das wissen, ist es ziemlich einfach, ein Objekt ohne Referenzen vollständig zu duplizieren. Hier ist eine Funktion zum Deepcopying grundlegender Datentypen (würde für benutzerdefinierte Klassen nicht funktionieren, aber Sie können diese jederzeit hinzufügen)

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

Pythons eigene integrierte Deepcopy basiert auf diesem Beispiel. Der einzige Unterschied besteht darin, dass es andere Typen unterstützt und Benutzerklassen unterstützt, indem die Attribute in eine neue doppelte Klasse dupliziert werden. Außerdem wird die unendliche Rekursion mit einem Verweis auf ein Objekt blockiert, das bereits mithilfe einer Memoliste oder eines Wörterbuchs angezeigt wird. Und das ist es wirklich, um tiefe Kopien zu machen. Im Kern werden beim Erstellen einer tiefen Kopie nur flache Kopien erstellt. Ich hoffe, diese Antwort fügt der Frage etwas hinzu.

BEISPIELE

Angenommen, Sie haben diese Liste: [1, 2, 3] . Die unveränderlichen Zahlen können nicht dupliziert werden, die andere Ebene jedoch. Sie können es mit einem Listenverständnis duplizieren: [x für x in [1, 2, 3]

Stellen Sie sich vor, Sie haben diese Liste: [[1, 2], [3, 4], [5, 6]] . Dieses Mal möchten Sie eine Funktion erstellen, die mithilfe der Rekursion alle Ebenen der Liste tief kopiert. Anstelle des vorherigen Listenverständnisses:

[x for x in _list]

Es wird eine neue für Listen verwendet:

[deepcopy_list(x) for x in _list]

Und deepcopy_list sieht so aus:

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

Dann haben Sie jetzt eine Funktion, mit der Sie jede Liste von Strs, Bools, Floast, Ints und sogar Listen mithilfe der Rekursion auf unendlich viele Ebenen vertiefen können . Und da haben Sie es, Deepcopying.

TLDR : Deepcopy verwendet die Rekursion zum Duplizieren von Objekten und gibt lediglich dieselben unveränderlichen Objekte wie zuvor zurück, da unveränderliche Objekte nicht dupliziert werden können. Es kopiert jedoch die innersten Schichten veränderlicher Objekte, bis es die äußerste veränderbare Schicht eines Objekts erreicht.

Corman
quelle
2

Eine leichte praktische Perspektive, um durch id und gc in die Erinnerung zu schauen.

>>> 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) 
B.Mr.W.
quelle
2

Denken Sie daran, dass in Python, wenn Sie:

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

List2 speichert nicht die aktuelle Liste, sondern einen Verweis auf list1. Wenn Sie also etwas mit list1 tun, ändert sich auch list2. Verwenden Sie das Kopiermodul (nicht standardmäßig, Download auf Pip), um eine Originalkopie der Liste zu erstellen ( copy.copy()für einfache Listen, copy.deepcopy()für verschachtelte). Dadurch wird eine Kopie erstellt, die sich mit der ersten Liste nicht ändert.

Dr. Hippo
quelle
0

Die Deepcopy-Option ist die einzige Methode, die für mich funktioniert:

from copy import deepcopy

a = [   [ list(range(1, 3)) for i in range(3) ]   ]
b = deepcopy(a)
b[0][1]=[3]
print('Deep:')
print(a)
print(b)
print('-----------------------------')
a = [   [ list(range(1, 3)) for i in range(3) ]   ]
b = a*1
b[0][1]=[3]
print('*1:')
print(a)
print(b)
print('-----------------------------')
a = [   [ list(range(1, 3)) for i in range(3) ] ]
b = a[:]
b[0][1]=[3]
print('Vector copy:')
print(a)
print(b)
print('-----------------------------')
a = [   [ list(range(1, 3)) for i in range(3) ]  ]
b = list(a)
b[0][1]=[3]
print('List copy:')
print(a)
print(b)
print('-----------------------------')
a = [   [ list(range(1, 3)) for i in range(3) ]  ]
b = a.copy()
b[0][1]=[3]
print('.copy():')
print(a)
print(b)
print('-----------------------------')
a = [   [ list(range(1, 3)) for i in range(3) ]  ]
b = a
b[0][1]=[3]
print('Shallow:')
print(a)
print(b)
print('-----------------------------')

führt zur Ausgabe von:

Deep:
[[[1, 2], [1, 2], [1, 2]]]
[[[1, 2], [3], [1, 2]]]
-----------------------------
*1:
[[[1, 2], [3], [1, 2]]]
[[[1, 2], [3], [1, 2]]]
-----------------------------
Vector copy:
[[[1, 2], [3], [1, 2]]]
[[[1, 2], [3], [1, 2]]]
-----------------------------
List copy:
[[[1, 2], [3], [1, 2]]]
[[[1, 2], [3], [1, 2]]]
-----------------------------
.copy():
[[[1, 2], [3], [1, 2]]]
[[[1, 2], [3], [1, 2]]]
-----------------------------
Shallow:
[[[1, 2], [3], [1, 2]]]
[[[1, 2], [3], [1, 2]]]
-----------------------------
shahar_m
quelle
0

Dies liegt daran, dass die Linie new_list = my_listeine neue Referenz zu der Variablen zuweist , my_listdie ist new_list mit dem Dies ist ähnlich CCode unten angegeben,

int my_list[] = [1,2,3,4];
int *new_list;
new_list = my_list;

Sie sollten das Kopiermodul verwenden, um eine neue Liste von zu erstellen

import copy
new_list = copy.deepcopy(my_list)
Roshin Raphel
quelle