Odpowiedzi:
Dzięki new_list = my_list
tak naprawdę nie masz dwóch list. Zadanie po prostu kopiuje odwołanie do listy, a nie do faktycznej listy, więc zarówno new_list
imy_list
odnoszą się do tej samej listy po cesji.
Aby faktycznie skopiować listę, masz różne możliwości:
Możesz użyć wbudowanej list.copy()
metody (dostępnej od Python 3.3):
new_list = old_list.copy()
Możesz pokroić:
new_list = old_list[:]
Opinia Alexa Martellego (przynajmniej w 2007 roku ) na ten temat jest taka, że jest to dziwna składnia i nigdy nie ma sensu jej używać . ;) (Jego zdaniem następny jest bardziej czytelny).
Możesz użyć wbudowanej list()
funkcji:
new_list = list(old_list)
Możesz użyć ogólnych copy.copy()
:
import copy
new_list = copy.copy(old_list)
Jest to trochę wolniejsze niż list()
dlatego, że najpierw musi znaleźć typ danych old_list
.
Jeśli lista zawiera obiekty i chcesz je również skopiować, użyj ogólnego copy.deepcopy()
:
import copy
new_list = copy.deepcopy(old_list)
Oczywiście najwolniejsza i najbardziej wymagająca pamięci metoda, ale czasem nieunikniona.
Przykład:
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))
Wynik:
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 już podał doskonałą odpowiedź, ale pomyślałem, że zrobię porównanie prędkości różnych metod:
copy.deepcopy(old_list)
Copy()
klasowe kopiowanie klas metodą pomocą deepcopyCopy()
metoda nie kopiująca klas (tylko dykta / listy / krotki)for item in old_list: new_list.append(item)
[i for i in old_list]
( zrozumienie listy )copy.copy(old_list)
list(old_list)
new_list = []; new_list.extend(old_list)
old_list[:]
( segmentacja list )Najszybszy jest więc podział list. Ale należy pamiętać, że copy.copy()
, list[:]
i list(list)
, w przeciwieństwie do copy.deepcopy()
a wersja Pythona nie kopiować żadnych list, słowniki i instancje klas w liście, więc jeśli oryginały zmieni, będą zmieniać się w skopiowanym liście też i odwrotnie.
(Oto skrypt, jeśli ktoś jest zainteresowany lub chce podnieść jakieś problemy :)
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
modułu. ponadto nie można wyciągać zbyt wielu wniosków z dowolnych mikrowymiarowych testów porównawczych takich jak ten.
[*old_list]
powinna ona być w przybliżeniu równoważna list(old_list)
, ale ponieważ jest ona składnią, a nie ogólnymi ścieżkami wywołań funkcji, zaoszczędzi trochę czasu wykonywania (i w przeciwieństwie do tego old_list[:]
, który nie wpisuje konwersji, [*old_list]
działa na każdym iterowalnym i tworzy a list
).
timeit
, przebiegi 50m zamiast 100k) patrz stackoverflow.com/a/43220129/3745896
[*old_list]
faktycznie wydaje się przewyższać prawie każdą inną metodę. (patrz moja odpowiedź połączona z poprzednimi komentarzami)
Mam mówiono , że Python 3.3+ dodajelist.copy()
metody, które powinny być jak najszybciej krojenia:
newlist = old_list.copy()
s.copy()
tworzy płytkie kopię s
(tak samo jak s[:]
).
python3.8
, .copy()
jest nieco szybszy niż krojenia. Zobacz poniżej odpowiedź @AaronsHall.
Jakie są opcje klonowania lub kopiowania listy w Pythonie?
W Pythonie 3 płytką kopię można wykonać za pomocą:
a_copy = a_list.copy()
W Pythonie 2 i 3 możesz uzyskać płytką kopię z pełnym wycięciem oryginału:
a_copy = a_list[:]
Istnieją dwa semantyczne sposoby kopiowania listy. Płytka kopia tworzy nową listę tych samych obiektów, głęboka kopia tworzy nową listę zawierającą nowe równoważne obiekty.
Płytka kopia kopiuje tylko samą listę, która jest kontenerem odniesień do obiektów na liście. Jeśli zawarte w nich obiekty można modyfikować, a jeden zostanie zmieniony, zmiana zostanie odzwierciedlona na obu listach.
Istnieją różne sposoby, aby to zrobić w Python 2 i 3. Sposoby Python 2 będą również działać w Python 3.
W Pythonie 2 idiomatycznym sposobem wykonania płytkiej kopii listy jest użycie pełnego wycinka oryginału:
a_copy = a_list[:]
Możesz również osiągnąć to samo, przekazując listę przez konstruktor listy,
a_copy = list(a_list)
ale użycie konstruktora jest mniej wydajne:
>>> timeit
>>> l = range(20)
>>> min(timeit.repeat(lambda: l[:]))
0.30504298210144043
>>> min(timeit.repeat(lambda: list(l)))
0.40698814392089844
W Pythonie 3 listy pobierają list.copy
metodę:
a_copy = a_list.copy()
W 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
Użycie nowej_listy = moja_lista następnie modyfikuje nową_listę za każdym razem, gdy zmienia się moja_lista. Dlaczego to?
my_list
to tylko nazwa wskazująca na rzeczywistą listę w pamięci. Kiedy powiesznew_list = my_list
że nie robisz kopii, dodajesz inną nazwę wskazującą na oryginalną listę w pamięci. Podobne problemy mogą występować podczas tworzenia kopii list.
>>> l = [[], [], []]
>>> l_copy = l[:]
>>> l_copy
[[], [], []]
>>> l_copy[0].append('foo')
>>> l_copy
[['foo'], [], []]
>>> l
[['foo'], [], []]
Lista jest tylko tablicą wskaźników do zawartości, więc płytka kopia po prostu kopiuje wskaźniki, więc masz dwie różne listy, ale mają tę samą zawartość. Aby wykonać kopię zawartości, potrzebujesz głębokiej kopii.
Aby zrobić głęboką kopię listy, w Pythonie 2 lub 3, użyj deepcopy
w copy
module :
import copy
a_deep_copy = copy.deepcopy(a_list)
Aby zademonstrować, w jaki sposób pozwala nam to tworzyć nowe listy podrzędne:
>>> import copy
>>> l
[['foo'], [], []]
>>> l_deep_copy = copy.deepcopy(l)
>>> l_deep_copy[0].pop()
'foo'
>>> l_deep_copy
[[], [], []]
>>> l
[['foo'], [], []]
Widzimy więc, że głęboko skopiowana lista jest zupełnie inną listą niż oryginał. Możesz rzucić własną funkcję - ale nie. Prawdopodobnie będziesz tworzyć błędy, których inaczej byś nie zrobił, korzystając ze standardowej funkcji kopiowania biblioteki.
eval
Możesz zobaczyć to jako sposób na głębokie kopiowanie, ale nie rób tego:
problematic_deep_copy = eval(repr(a_list))
W 64-bitowym języku 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
w 64-bitowym języku 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))
i jest znacznie szybszy.
Istnieje już wiele odpowiedzi, które mówią, jak zrobić właściwą kopię, ale żadna z nich nie wyjaśnia, dlaczego oryginalna „kopia” nie powiodła się.
Python nie przechowuje wartości w zmiennych; wiąże nazwy z obiektami. Twoje pierwotne zadanie obejmowało obiekt, o którym mowa, my_list
i wiązało je new_list
również. Bez względu na to, jakiej nazwy użyjesz, wciąż jest tylko jedna lista, więc zmiany dokonane w odniesieniu do niej my_list
będą obowiązywać, gdy będziesz ją określać jako new_list
. Każda z pozostałych odpowiedzi na to pytanie daje różne sposoby tworzenia nowego obiektu do powiązania new_list
.
Każdy element listy działa jak nazwa, ponieważ każdy element wiąże się nie tylko z obiektem. Płytka kopia tworzy nową listę, której elementy wiążą się z tymi samymi obiektami, co poprzednio.
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]
Aby przesunąć listę o krok dalej, skopiuj każdy obiekt, do którego odnosi się twoja lista, i powiąż te kopie elementów z nową listą.
import copy
# each element must have __copy__ defined for this...
new_list = [copy.copy(element) for element in my_list]
Nie jest to jeszcze głęboka kopia, ponieważ każdy element listy może odnosić się do innych obiektów, tak jak lista jest związana z jej elementami. Aby rekurencyjnie skopiować każdy element na liście, a następnie każdy inny obiekt, do którego odnosi się każdy element, i tak dalej: wykonaj głęboką kopię.
import copy
# each element must have __deepcopy__ defined for this...
new_list = copy.deepcopy(my_list)
Zobacz dokumentację uzyskać więcej informacji o przypadkach narożnych w kopiowaniu.
Posługiwać się thing[:]
>>> a = [1,2]
>>> b = a[:]
>>> a += [3]
>>> a
[1, 2, 3]
>>> b
[1, 2]
>>>
Zacznijmy od początku i zbadaj to pytanie.
Załóżmy, że masz dwie listy:
list_1=['01','98']
list_2=[['01','98']]
I musimy skopiować obie listy, zaczynając teraz od pierwszej listy:
Spróbujmy najpierw, ustawiając zmienną copy
na naszą oryginalną listę list_1
:
copy=list_1
Teraz, jeśli myślisz, że kopia skopiowała listę_1, to się mylisz. id
Funkcja może pokazać nam, jeśli dwie zmienne mogą wskazywać na ten sam obiekt. Spróbujmy tego:
print(id(copy))
print(id(list_1))
Dane wyjściowe to:
4329485320
4329485320
Obie zmienne są dokładnie tym samym argumentem. Czy jesteś zaskoczony?
Skoro wiemy, że Python nie przechowuje niczego w zmiennej, Zmienne odnoszą się tylko do obiektu, a obiekt przechowuje wartość. Tutaj przedmiotem jestlist
ale utworzyliśmy dwa odwołania do tego samego obiektu przez dwie różne nazwy zmiennych. Oznacza to, że obie zmienne wskazują ten sam obiekt, tylko o różnych nazwach.
Kiedy to robisz copy=list_1
, robi to:
Tutaj na liście obrazków_1 i kopii są dwie nazwy zmiennych, ale obiekt jest taki sam dla obu zmiennych, które są list
Więc jeśli spróbujesz zmodyfikować skopiowaną listę, zmodyfikuje ona również oryginalną listę, ponieważ lista jest tylko jedna, zmodyfikujesz tę listę bez względu na to, czy zrobisz to z listy skopiowanej lub z oryginalnej listy:
copy[0]="modify"
print(copy)
print(list_1)
wynik:
['modify', '98']
['modify', '98']
Więc zmodyfikował oryginalną listę:
Przejdźmy teraz do pythonowej metody kopiowania list.
copy_1=list_1[:]
Ta metoda rozwiązuje pierwszy problem, który mieliśmy:
print(id(copy_1))
print(id(list_1))
4338792136
4338791432
Jak widzimy, nasza lista ma różne identyfikatory i oznacza to, że obie zmienne wskazują różne obiekty. Więc tak naprawdę dzieje się tutaj:
Teraz spróbujmy zmodyfikować listę i zobaczmy, czy nadal mamy do czynienia z poprzednim problemem:
copy_1[0]="modify"
print(list_1)
print(copy_1)
Dane wyjściowe to:
['01', '98']
['modify', '98']
Jak widać, zmodyfikował tylko skopiowaną listę. To znaczy, że zadziałało.
Myślisz, że skończyliśmy? Nie. Spróbujmy skopiować naszą listę zagnieżdżoną.
copy_2=list_2[:]
list_2
powinien odnosić się do innego obiektu, który jest kopią list_2
. Sprawdźmy:
print(id((list_2)),id(copy_2))
Otrzymujemy wynik:
4330403592 4330403528
Teraz możemy założyć, że obie listy wskazują inny obiekt, więc spróbujmy go zmodyfikować i zobaczmy, że daje to, co chcemy:
copy_2[0][1]="modify"
print(list_2,copy_2)
To daje nam wynik:
[['01', 'modify']] [['01', 'modify']]
Może się to wydawać nieco mylące, ponieważ zadziałała ta sama metoda, którą poprzednio stosowaliśmy. Spróbujmy to zrozumieć.
Kiedy to zrobisz:
copy_2=list_2[:]
Kopiujesz tylko listę zewnętrzną, a nie wewnętrzną. Możemy użyć tej id
funkcji jeszcze raz, aby to sprawdzić.
print(id(copy_2[0]))
print(id(list_2[0]))
Dane wyjściowe to:
4329485832
4329485832
Kiedy to robimy copy_2=list_2[:]
, dzieje się tak:
Tworzy kopię listy, ale tylko zewnętrzną kopię listy, a nie kopię listy zagnieżdżonej, lista zagnieżdżona jest taka sama dla obu zmiennych, więc jeśli spróbujesz zmodyfikować listę zagnieżdżoną, zmodyfikuje również oryginalną listę, ponieważ obiekt listy zagnieżdżonej jest taki sam dla obu list.
Jakie jest rozwiązanie? Rozwiązaniem jest deepcopy
funkcja.
from copy import deepcopy
deep=deepcopy(list_2)
Sprawdźmy to:
print(id((list_2)),id(deep))
4322146056 4322148040
Obie listy zewnętrzne mają różne identyfikatory, spróbujmy tego na wewnętrznych listach zagnieżdżonych.
print(id(deep[0]))
print(id(list_2[0]))
Dane wyjściowe to:
4322145992
4322145800
Jak widać oba identyfikatory są różne, co oznacza, że możemy założyć, że obie listy zagnieżdżone wskazują teraz inny obiekt.
Oznacza to, że kiedy robisz to, deep=deepcopy(list_2)
co się naprawdę dzieje:
Obie listy zagnieżdżone wskazują inny obiekt i mają teraz osobną kopię listy zagnieżdżonej.
Teraz spróbujmy zmodyfikować listę zagnieżdżoną i zobaczmy, czy rozwiązała poprzedni problem, czy nie:
deep[0][1]="modify"
print(list_2,deep)
Wyprowadza:
[['01', '98']] [['01', 'modify']]
Jak widać, nie zmodyfikował oryginalnej listy zagnieżdżonej, tylko zmodyfikował skopiowaną listę.
Oto wyniki synchronizacji przy użyciu Pythona 3.6.8. Pamiętaj, że czasy te są względem siebie względne, a nie absolutne.
Trzymałem się tylko robienia płytkich kopii, a także dodałem kilka nowych metod, które nie były możliwe w Python2, takich jak list.copy()
( odpowiednik wycinka Python3 ) i dwie formy rozpakowywania listy ( *new_list, = list
i 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
Widzimy, że zwycięzca Python2 nadal dobrze sobie radzi, ale nie wyrówna Python3 list.copy()
zbyt wiele , zwłaszcza biorąc pod uwagę jego lepszą czytelność.
Ciemny koń to metoda rozpakowywania i przepakowywania ( b = [*a]
), która jest ~ 25% szybsza niż surowe krojenie i ponad dwukrotnie szybsza niż inna metoda rozpakowywania (*b, = a
).
b = a * 1
robi też zaskakująco dobrze.
Zauważ, że te metody nie dają równoważnych wyników dla danych wejściowych innych niż listy. Wszystkie działają dla obiektów, które można wycinać, kilka dla dowolnej iterowalnej, ale copy.copy()
działa tylko dla bardziej ogólnych obiektów Pythona.
Oto kod testowy dla zainteresowanych stron ( szablon stąd ):
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]
- jedyny oczywisty sposób, aby to zrobić;).
Wszyscy inni współautorzy udzielili świetnych odpowiedzi, które działają, gdy masz listę z jednym wymiarem (wyrównywaną), jednak metody wspomniane do tej pory copy.deepcopy()
działają tylko w celu klonowania / kopiowania listy, a nie wskazują na zagnieżdżone list
obiekty, gdy jesteś praca z wielowymiarowymi, zagnieżdżonymi listami (lista list). Podczas gdy Felix Kling odwołuje się do tego w swojej odpowiedzi, jest nieco więcej w tym problemie i możliwe jest obejście problemu za pomocą wbudowanych rozwiązań, które mogą okazać się szybszą alternatywą deepcopy
.
Chociaż new_list = old_list[:]
, copy.copy(old_list)'
a dla Py3k old_list.copy()
pracy na listach pojedynczych wyrównane, oni przywrócić wskazując na list
obiekty zagnieżdżone w obrębie old_list
a new_list
, a zmiany do jednego z list
obiektów są utrwalać w drugiej.
Jak zauważyli zarówno Aaron Hall, jak i PM 2Ring, używanie
eval()
nie jest tylko złym pomysłem, ale jest również znacznie wolniejsze niżcopy.deepcopy()
.Oznacza to, że w przypadku list wielowymiarowych jedyną opcją jest
copy.deepcopy()
. Biorąc to pod uwagę, tak naprawdę nie jest to możliwe, ponieważ wydajność idzie daleko na południe, gdy próbujesz użyć go na średniej wielkości tablicy wielowymiarowej. Starałem siętimeit
użyć tablicy 42x42, która nie była niespotykana ani nawet tak duża w aplikacjach bioinformatycznych. Zrezygnowałem z oczekiwania na odpowiedź i zacząłem pisać swoją edycję w tym poście.Wydaje się, że jedyną realną opcją jest wówczas inicjalizacja wielu list i praca nad nimi niezależnie. Jeśli ktoś ma inne sugestie, jak radzić sobie z wielowymiarowym kopiowaniem list, byłoby to mile widziane.
Jak stwierdzili inni, przy korzystaniu z modułu i list wielowymiarowych występują poważne problemy z wydajnością .copy
copy.deepcopy
repr()
wystarczy do odtworzenia obiektu. Ponadto, eval()
jest narzędziem ostateczności; zobacz Eval naprawdę niebezpieczny dla weterana SO Neda Batcheldera, aby uzyskać szczegółowe informacje. Dlatego kiedy opowiadasz się za użyciem eval()
, naprawdę powinieneś wspomnieć, że może być niebezpieczne.
eval()
ogólnie posiadanie funkcji w Pythonie jest ryzykowne. Nie chodzi o to, czy używasz funkcji w kodzie, ale o to, że jest to luka bezpieczeństwa w Pythonie sama w sobie. Mój przykład nie używa go z funkcji, która odbiera sygnał z input()
, sys.agrv
lub nawet pliku tekstowego. Bardziej przypomina to jednorazowe inicjowanie pustej listy wielowymiarowej, a następnie kopiowanie jej w pętli zamiast ponownego inicjowania przy każdej iteracji pętli.
new_list = eval(repr(old_list))
, więc poza tym, że jest to zły pomysł, prawdopodobnie działa zbyt wolno.
Zaskakuje mnie, że o tym jeszcze nie wspomniano, więc dla kompletności ...
Możesz wykonać rozpakowanie listy za pomocą „operatora splat”:, *
który również skopiuje elementy listy.
old_list = [1, 2, 3]
new_list = [*old_list]
new_list.append(4)
old_list == [1, 2, 3]
new_list == [1, 2, 3, 4]
Oczywistym minusem tej metody jest to, że jest ona dostępna tylko w Python 3.5+.
Jeśli chodzi o czas, wydaje się, że działa to lepiej niż inne popularne metody.
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
i new_list
są dwie różne listy, edytując jeden nie zmieni drugiego (chyba że jesteś bezpośrednio mutacji same elementy (takie jak listy listy), żadna z tych metod, są głębokie kopie).
W już udzielonych odpowiedziach brakowało bardzo prostego podejścia niezależnego od wersji Pythona, z którego można korzystać przez większość czasu (przynajmniej ja):
new_list = my_list * 1 #Solution 1 when you are not using nested lists
Jeśli jednak moja_lista zawiera inne kontenery (np. Listy zagnieżdżone), musisz użyć deepcopy, jak sugerują inni w powyższych odpowiedziach z biblioteki kopii. Na przykład:
import copy
new_list = copy.deepcopy(my_list) #Solution 2 when you are using nested lists
. Premia : Jeśli nie chcesz kopiować elementów, użyj (czyli płytkiej kopii):
new_list = my_list[:]
Rozumiemy różnicę między rozwiązaniem nr 1 a rozwiązaniem nr 2
>>> a = range(5)
>>> b = a*1
>>> a,b
([0, 1, 2, 3, 4], [0, 1, 2, 3, 4])
>>> a[2] = 55
>>> a,b
([0, 1, 55, 3, 4], [0, 1, 2, 3, 4])
Jak widać Rozwiązanie nr 1 działało idealnie, gdy nie korzystaliśmy z list zagnieżdżonych. Sprawdźmy, co się stanie, gdy zastosujemy rozwiązanie nr 1 do zagnieżdżonych list.
>>> 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
Zauważ, że istnieją przypadki, w których jeśli zdefiniowałeś własną klasę niestandardową i chcesz zachować atrybuty, powinieneś użyć copy.copy()
lub copy.deepcopy()
zamiast alternatyw, na przykład w Pythonie 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))
Wyjścia:
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
Spróbuj to zrozumieć. Powiedzmy, że moja_lista znajduje się w pamięci sterty w lokalizacji X, tzn. Moja_lista wskazuje na X. Teraz poprzez przypisanienew_list = my_list
Ci Pozwalanie nowej_listy wskazuje na X. Jest to znane jako płytka kopia.
Teraz, jeśli przypisasz new_list = my_list[:]
, po prostu kopiujesz każdy obiekt z mojej_listy na nową_listę. Jest to znane jako Deep copy.
Innym sposobem na to jest:
new_list = list(old_list)
import copy
new_list = copy.deepcopy(old_list)
Chciałem opublikować coś nieco innego niż niektóre inne odpowiedzi. Mimo że najprawdopodobniej nie jest to najbardziej zrozumiała lub najszybsza opcja, zapewnia ona nieco wewnętrzny obraz działania głębokiej kopii, a także jest inną alternatywą dla głębokiego kopiowania. Naprawdę nie ma znaczenia, czy moja funkcja ma błędy, ponieważ chodzi o to, aby pokazać sposób kopiowania obiektów, takich jak odpowiedzi na pytania, ale także użyć tego jako punktu, aby wyjaśnić, jak działa funkcja głębokiego kopiowania.
U podstaw każdej funkcji głębokiego kopiowania leży sposób na wykonanie płytkiej kopii. W jaki sposób? Prosty. Każda funkcja głębokiego kopiowania powiela jedynie kontenery niezmiennych obiektów. Podczas głębokiego kopiowania listy zagnieżdżonej kopiowane są tylko listy zewnętrzne, a nie zmienne obiekty wewnątrz list. Kopiujesz tylko pojemniki. To samo działa również w przypadku klas. Kiedy głęboko kopiujesz klasę, kopiujesz wszystkie jej zmienne atrybuty. Więc jak? Dlaczego musisz kopiować tylko kontenery, takie jak listy, dykty, krotki, itery, klasy i instancje klas?
To proste. Zmiennego obiektu nie można tak naprawdę powielić. Nigdy nie można go zmienić, więc jest to tylko jedna wartość. Oznacza to, że nigdy nie musisz duplikować ciągów, liczb, booli ani żadnego z nich. Ale jak powielibyście pojemniki? Prosty. Dokonujesz właśnie inicjalizacji nowego kontenera ze wszystkimi wartościami. Deepcopy polega na rekurencji. Powielają wszystkie pojemniki, nawet te z pojemnikami w nich, dopóki nie pozostaną żadne pojemniki. Kontener jest niezmiennym przedmiotem.
Kiedy już to wiesz, całkowite powielenie obiektu bez żadnych odniesień jest dość łatwe. Oto funkcja do głębokiego kopiowania podstawowych typów danych (nie działałaby dla klas niestandardowych, ale zawsze można to dodać)
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))
Wbudowana głęboka kopia Pythona oparta jest na tym przykładzie. Jedyną różnicą jest to, że obsługuje inne typy, a także obsługuje klasy użytkowników poprzez duplikowanie atrybutów w nową zduplikowaną klasę, a także blokuje nieskończoną rekurencję w odniesieniu do obiektu, który już widział za pomocą listy notatek lub słownika. I to naprawdę wszystko, aby robić głębokie kopie. U podstaw robienia głębokiej kopii jest po prostu robienie płytkich kopii. Mam nadzieję, że ta odpowiedź doda coś do pytania.
PRZYKŁADY
Powiedz, że masz tę listę: [1, 2, 3] . Niezmiennych liczb nie można powielać, ale drugą warstwę można. Możesz go zduplikować, używając rozumienia listy: [x dla x w [1, 2, 3]
Teraz wyobraź sobie, że masz tę listę: [[1, 2], [3, 4], [5, 6]] . Tym razem chcesz stworzyć funkcję, która używa rekurencji do głębokiego kopiowania wszystkich warstw listy. Zamiast poprzedniego zrozumienia listy:
[x for x in _list]
Używa nowego dla list:
[deepcopy_list(x) for x in _list]
I deepcopy_list wygląda następująco:
def deepcopy_list(x):
if isinstance(x, (str, bool, float, int)):
return x
else:
return [deepcopy_list(y) for y in x]
Teraz masz funkcję, która może głęboko kopiować dowolną listę łańcuchów, bool, floast, ints, a nawet list do nieskończenie wielu warstw za pomocą rekurencji. I oto masz, głębokie kopiowanie.
TLDR : Deepcopy używa rekurencji do powielania obiektów i jedynie zwraca te same obiekty niezmienne jak poprzednio, ponieważ obiektów niezmiennych nie można powielić. Jednak wykonuje głębokie kopie najbardziej wewnętrznych warstw obiektów podlegających zmianom, dopóki nie osiągnie najbardziej zewnętrznej możliwej do zmiany warstwy obiektu.
Lekka praktyczna perspektywa spojrzenia w pamięć poprzez id i gc.
>>> b = a = ['hell', 'word']
>>> c = ['hell', 'word']
>>> id(a), id(b), id(c)
(4424020872, 4424020872, 4423979272)
| |
-----------
>>> id(a[0]), id(b[0]), id(c[0])
(4424018328, 4424018328, 4424018328) # all referring to same 'hell'
| | |
-----------------------
>>> id(a[0][0]), id(b[0][0]), id(c[0][0])
(4422785208, 4422785208, 4422785208) # all referring to same 'h'
| | |
-----------------------
>>> a[0] += 'o'
>>> a,b,c
(['hello', 'word'], ['hello', 'word'], ['hell', 'word']) # b changed too
>>> id(a[0]), id(b[0]), id(c[0])
(4424018384, 4424018384, 4424018328) # augmented assignment changed a[0],b[0]
| |
-----------
>>> b = a = ['hell', 'word']
>>> id(a[0]), id(b[0]), id(c[0])
(4424018328, 4424018328, 4424018328) # the same hell
| | |
-----------------------
>>> import gc
>>> gc.get_referrers(a[0])
[['hell', 'word'], ['hell', 'word']] # one copy belong to a,b, the another for c
>>> gc.get_referrers(('hell'))
[['hell', 'word'], ['hell', 'word'], ('hell', None)] # ('hello', None)
Pamiętaj o tym w Pythonie, gdy:
list1 = ['apples','bananas','pineapples']
list2 = list1
List2 nie przechowuje faktycznej listy, ale odniesienie do list1. Więc kiedy robisz cokolwiek do list1, lista2 również się zmienia. użyj modułu kopiowania (nie domyślnie, pobierz na pip), aby wykonać oryginalną kopię listy ( copy.copy()
dla list prostych, copy.deepcopy()
dla zagnieżdżonych). To tworzy kopię, która nie zmienia się z pierwszą listą.
Opcja głębokiej kopii jest jedyną metodą, która działa dla mnie:
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('-----------------------------')
prowadzi do wyjścia:
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]]]
-----------------------------
newlist = [*mylist]
istnieje również możliwość w Pythonie 3. Byćnewlist = list(mylist)
może jest to jednak bardziej jasne.