Jakie są opcje klonowania lub kopiowania listy w Pythonie?
Podczas używania new_list = my_list
wszelkie modyfikacje new_list
zmian my_list
za każdym razem. Dlaczego to?
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)]
newlist = [*mylist]
istnieje również możliwość w Pythonie 3. Byćnewlist = list(mylist)
może jest to jednak bardziej jasne.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[:]
ilist(list)
, w przeciwieństwie docopy.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 :)
źródło
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żnalist(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 tegoold_list[:]
, który nie wpisuje konwersji,[*old_list]
działa na każdym iterowalnym i tworzy alist
).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+ dodaje
list.copy()
metody, które powinny być jak najszybciej krojenia:newlist = old_list.copy()
źródło
s.copy()
tworzy płytkie kopięs
(tak samo jaks[:]
).python3.8
,.copy()
jest nieco szybszy niż krojenia. Zobacz poniżej odpowiedź @AaronsHall.W Pythonie 3 płytką kopię można wykonać za pomocą:
W Pythonie 2 i 3 możesz uzyskać płytką kopię z pełnym wycięciem oryginału:
Wyjaśnienie
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 listy
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.
Python 2
W Pythonie 2 idiomatycznym sposobem wykonania płytkiej kopii listy jest użycie pełnego wycinka oryginału:
Możesz również osiągnąć to samo, przekazując listę przez konstruktor listy,
ale użycie konstruktora jest mniej wydajne:
Python 3
W Pythonie 3 listy pobierają
list.copy
metodę:W Python 3.5:
Utworzenie innego wskaźnika nie powoduje wykonania kopii
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.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.
Głębokie kopie
Aby zrobić głęboką kopię listy, w Pythonie 2 lub 3, użyj
deepcopy
wcopy
module :Aby zademonstrować, w jaki sposób pozwala nam to tworzyć nowe listy podrzędne:
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.
Nie używaj
eval
Możesz zobaczyć to jako sposób na głębokie kopiowanie, ale nie rób tego:
W 64-bitowym języku Python 2.7:
w 64-bitowym języku Python 3.5:
źródło
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 jenew_list
również. Bez względu na to, jakiej nazwy użyjesz, wciąż jest tylko jedna lista, więc zmiany dokonane w odniesieniu do niejmy_list
będą obowiązywać, gdy będziesz ją określać jakonew_list
. Każda z pozostałych odpowiedzi na to pytanie daje różne sposoby tworzenia nowego obiektu do powiązanianew_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.
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ą.
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ę.
Zobacz dokumentację uzyskać więcej informacji o przypadkach narożnych w kopiowaniu.
źródło
Posługiwać się
thing[:]
źródło
Zacznijmy od początku i zbadaj to pytanie.
Załóżmy, że masz dwie listy:
I musimy skopiować obie listy, zaczynając teraz od pierwszej listy:
Spróbujmy najpierw, ustawiając zmienną
copy
na naszą oryginalną listę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:Dane wyjściowe to:
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 jest
list
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:
wynik:
Więc zmodyfikował oryginalną listę:
Przejdźmy teraz do pythonowej metody kopiowania list.
Ta metoda rozwiązuje pierwszy problem, który mieliśmy:
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:
Dane wyjściowe to:
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ą.
list_2
powinien odnosić się do innego obiektu, który jest kopiąlist_2
. Sprawdźmy:Otrzymujemy wynik:
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:
To daje nam wynik:
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:
Kopiujesz tylko listę zewnętrzną, a nie wewnętrzną. Możemy użyć tej
id
funkcji jeszcze raz, aby to sprawdzić.Dane wyjściowe to:
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.Sprawdźmy to:
Obie listy zewnętrzne mają różne identyfikatory, spróbujmy tego na wewnętrznych listach zagnieżdżonych.
Dane wyjściowe to:
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:
Wyprowadza:
Jak widać, nie zmodyfikował oryginalnej listy zagnieżdżonej, tylko zmodyfikował skopiowaną listę.
źródło
Idiomem tego języka jest Python
newList = oldList[:]
źródło
Czasy Python 3.6
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
inew_list = [*list]
):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 ):
źródło
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żonelist
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 Py3kold_list.copy()
pracy na listach pojedynczych wyrównane, oni przywrócić wskazując nalist
obiekty zagnieżdżone w obrębieold_list
anew_list
, a zmiany do jednego zlist
obiektów są utrwalać w drugiej.Edycja: Nowe informacje ujawnione
Jak stwierdzili inni, przy korzystaniu z modułu i list wielowymiarowych występują poważne problemy z wydajnością .
copy
copy.deepcopy
źródło
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życiemeval()
, 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ł zinput()
,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.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.
źródło
old_list
inew_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):
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:
. Premia : Jeśli nie chcesz kopiować elementów, użyj (czyli płytkiej kopii):
Rozumiemy różnicę między rozwiązaniem nr 1 a rozwiązaniem nr 2
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.
źródło
Zauważ, że istnieją przypadki, w których jeśli zdefiniowałeś własną klasę niestandardową i chcesz zachować atrybuty, powinieneś użyć
copy.copy()
lubcopy.deepcopy()
zamiast alternatyw, na przykład w Pythonie 3:Wyjścia:
źródło
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)
źródło
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ć)
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:
Używa nowego dla list:
I deepcopy_list wygląda następująco:
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.
źródło
Lekka praktyczna perspektywa spojrzenia w pamięć poprzez id i gc.
źródło
Pamiętaj o tym w Pythonie, gdy:
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ą.źródło
Opcja głębokiej kopii jest jedyną metodą, która działa dla mnie:
prowadzi do wyjścia:
źródło