Liste Änderungen unerwartet nach Zuweisung. Wie klone oder kopiere ich es, um dies zu verhindern?
Während der Verwendung new_list = my_list
werden Änderungen an new_list
Änderungen my_list
jedes Mal vorgenommen. Warum ist das so und wie kann ich die Liste klonen oder kopieren, um dies zu verhindern?
Antworten
Mit new_list = my_list
haben Sie eigentlich nicht zwei Listen. Die Zuweisung kopiert nur den Verweis auf die Liste, nicht die tatsächliche Liste, also beide new_list
und my_list
verweist 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 generisches verwenden copy.copy():
import copy new_list = copy.copy(old_list)
Dies ist etwas langsamer als
list()
weil es zuerst den Datentyp von herausfinden mussold_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 gab bereits eine ausgezeichnete Antwort, aber ich dachte, ich würde einen Geschwindigkeitsvergleich der verschiedenen Methoden durchführen:
- 10,59 s (105,9 us / itn) - copy.deepcopy(old_list)
- 10,16 Sek. (101,6us / itn) - reine Python-
Copy()
Methode zum Kopieren von Klassen mit Deepcopy - 1.488 Sek. (14.88us / itn) - reine Python-
Copy()
Methode, die keine Klassen kopiert (nur Dikte / Listen / Tupel) - 0,325 s (3,25 us / itn) -
for item in old_list: new_list.append(item)
- 0,217 Sek. (2,17us / itn) -
[i for i in old_list]
(ein Listenverständnis ) - 0,186 s (1,86 us / itn) - copy.copy(old_list)
- 0,075 s (0,75 us / itn) -
list(old_list)
- 0,053 s (0,53 us / itn) -
new_list = []; new_list.extend(old_list)
- 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
Mir wurde gesagt, dass Python 3.3+ einelist.copy() Methode hinzufügt , die so schnell wie das Schneiden sein sollte:
newlist = old_list.copy()
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 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 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 Methode zum Erstellen einer flachen Kopie einer Liste in einem vollständigen Ausschnitt des Originals:
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.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
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_list
ist nur ein Name, der auf die tatsächliche Liste im Speicher verweist. Wenn Sie sagen, dass new_list = my_list
Sie 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))
- Dies ist gefährlich, insbesondere wenn Sie etwas aus einer Quelle bewerten, der Sie nicht vertrauen.
- Es ist nicht zuverlässig, wenn ein Unterelement, das Sie kopieren, keine Darstellung hat, die ausgewertet werden kann, um ein äquivalentes Element zu reproduzieren.
- 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
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_list
gebunden 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_list
werden, 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 .
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 copy
auf unsere ursprüngliche Liste zu setzen list_1
:
copy=list_1
Wenn Sie nun denken, dass kopieren Sie die Liste_1 kopiert haben, dann liegen Sie falsch. Die id
Funktion 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, list
aber wir haben zwei Verweise auf dasselbe Objekt mit zwei verschiedenen Variablennamen erstellt. Dies bedeutet, dass beide Variablen auf dasselbe Objekt zeigen, nur mit unterschiedlichen Namen.
Wenn Sie dies tun copy=list_1
, geschieht Folgendes:
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 eigentlich los ist, ist:
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_2
sollte auf ein anderes Objekt verweisen, von dem eine Kopie ist list_2
. Lass uns das Prüfen:
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 zeigen. Versuchen wir nun, es zu ändern, und sehen wir, dass es das gibt, 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 id
Funktion 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:
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 deepcopy
Funktion.
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:
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 können, wurde die ursprünglich verschachtelte Liste nicht geändert, sondern nur die kopierte Liste.
Benutzen thing[:]
>>> a = [1,2]
>>> b = a[:]
>>> a += [3]
>>> a
[1, 2, 3]
>>> b
[1, 2]
>>>
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 nur an flache Kopien gehalten 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, = list
und 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 alle funktionieren 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))
Pythons Redewendung dafür ist newList = oldList[:]
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 list
Objekte 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 list
innerhalb der verschachtelten Objekte old_list
und die new_list
, und Änderungen an einem der list
Objekte 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 alscopy.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,timeit
ein 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 copy
Moduls und copy.deepcopy
bei mehrdimensionalen Listen erhebliche Leistungsprobleme auf .
Es überrascht mich, dass dies noch nicht erwähnt wurde, der Vollständigkeit halber ...
Sie können das Entpacken der Liste 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)
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
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
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_list
Indem 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)
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 Fragenantworten zu kopieren, aber dies auch als Punkt zu verwenden, um zu erklären, wie Deepcopy im Kern funktioniert.
Im Zentrum 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 darin, 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 werden jedoch die innersten Schichten veränderlicher Objekte tief kopiert, bis die äußerste veränderbare Schicht eines Objekts erreicht ist.
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)
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.
Dies liegt daran, dass die Linie new_list = my_list
eine neue Referenz zu der Variablen zuweist , my_list
die ist new_list
mit dem Dies ist ähnlich C
Code 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)
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]]]
-----------------------------