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 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 gab bereits eine ausgezeichnete Antwort, aber ich dachte, ich würde einen Geschwindigkeitsvergleich der verschiedenen Methoden durchführen:
copy.deepcopy(old_list)
Copy()
Methode zum Kopieren von Klassen mit DeepcopyCopy()
Methode, die keine Klassen kopiert (nur Dikte / Listen / Tupel)for item in old_list: new_list.append(item)
[i for i in old_list]
(ein Listenverständnis )copy.copy(old_list)
list(old_list)
new_list = []; new_list.extend(old_list)
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
timeit
Modul. Außerdem kann man aus solchen willkürlichen Mikro-Benchmarks nicht viel schließen.
[*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
).
[*old_list]
scheint tatsächlich fast jede andere Methode zu übertreffen. (siehe meine Antwort in früheren Kommentaren verlinkt)
Mir wurde gesagt, dass Python 3.3+ einelist.copy()
Methode hinzufügt , die so schnell wie das Schneiden sein sollte:
newlist = old_list.copy()
s.copy()
schafft eine flache Kopie von s
(gleich wie s[:]
).
python3.8
, .copy()
ist etwas schneller als Slicing. Siehe unten @AaronsHall Antwort.
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[:]
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.
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.
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
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 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.
Verwenden Sie deepcopy
im copy
Modul 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.
eval
Sie können dies als einen Weg zur Deepcopy sehen, aber tun Sie es nicht:
problematic_deep_copy = eval(repr(a_list))
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
list_copy=[]
for item in list: list_copy.append(copy(item))
und es ist viel schneller.
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 .
Verwenden thing[:]
>>> a = [1,2]
>>> b = a[:]
>>> a += [3]
>>> a
[1, 2, 3]
>>> b
[1, 2]
>>>
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 jetzt denken, kopieren Sie die Liste_1, 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 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:
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:
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 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 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, wurde die ursprünglich verschachtelte Liste nicht geändert, sondern nur die kopierte Liste.
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, = 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 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))
b=[*a]
- der eine offensichtliche Weg, dies zu tun;).
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.
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 .
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.
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.agrv
oder 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.
new_list = eval(repr(old_list))
, dass es eine schlechte Idee ist, ist es wahrscheinlich auch viel zu langsam, um zu arbeiten.
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)
old_list
und new_list
sind 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).
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 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.
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.
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]]]
-----------------------------
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)
newlist = [*mylist]
ist auch eine Möglichkeit in Python 3.newlist = list(mylist)
Vielleicht ist es aber klarer.