Historia pytania: W Pythonie kopiowanie obiektów (szczególnie kolekcji — list, słowników) jest krytyczne: proste przypisanie zmiennej tworzy nowy link, a nie kopię. Specjalne metody copy() i deepcopy() pojawiły się, aby uniknąć niepożądanych "efektów ubocznych" przy wspólnym używaniu struktur.
Problem: Przy pracy z zagnieżdżonymi kolekcjami (lista list, słownik w słowniku) proste copy() reprodukuje tylko sam kontener, ale nie wewnętrzne elementy. Może to prowadzić do trudnych do uchwycenia błędów: zmiana zagnieżdżonego elementu odzwierciedla się we wszystkich "kopiach".
Rozwiązanie: copy.copy() tworzy płytką kopię (shallow copy) — nowy kontener najwyższego poziomu, ale zagnieżdżone obiekty pozostają te same. copy.deepcopy() rekurencyjnie kopiuje wszystkie zagnieżdżone obiekty.
Przykład kodu:
import copy lst = [[1, 2], [3, 4]] shallow = copy.copy(lst) deep = copy.deepcopy(lst) lst[0][0] = 10 print(shallow) # [[10, 2], [3, 4]] — zagnieżdżony obiekt zmienił się! print(deep) # [[1, 2], [3, 4]] — deepcopy zachował pierwotny stan
Kluczowe cechy:
Czy czasami wystarczy napisać lst2 = lst[:] aby skopiować listę?
lst2 = lst[:] tworzy płytką kopię listy, ale zagnieżdżone obiekty (np. listy w liście) będą nadal te same. Dla płaskich list — to wystarczy; dla zagnieżdżonych struktur — nie.
Przykład:
lst = [[1], [2]] lst2 = lst[:] lst[0][0] = 99 print(lst2) # [[99], [2]] — zagnieżdżony element zmienił się w obu "kopiach"
Czy metoda .copy() działa tak samo dla wszystkich kolekcji?
Nie. Na przykład, dict.copy() działa jako płytkie kopiowanie, list.copy() pojawił się dopiero w Pythonie 3.3, dla set — jest set.copy(). Dla obiektów użytkowników wsparcie .copy() zależy od zaimplementowanej metody.
Czy można obejść się bez deepcopy wszędzie? Czy to jest bezpieczne i wydajne?
Nie. deepcopy to kosztowna operacja: może powodować problemy z wydajnością, szczególnie na dużych strukturach, a także łamie się na obiektach niekopiowalnych/zwrotnych lub niestandardowych. Używaj deepcopy tylko wtedy, gdy naprawdę potrzebujesz w pełni niezależnego, rekurencyjnego kopiowania.
W testach robiliśmy kopiowanie "słownika słowników" przez dict.copy(). Z tego powodu poprawki w zagnieżdżonej strukturze dla jednego użytkownika nagle dotykały inne testy (dane mutowały się globalnie).
Zalety:
Wady:
Wdrożono copy.deepcopy(), opisano w dokumentacji poziom zagnieżdżenia i cechy struktury każdego obiektu, unikano deepcopy dla dużych objętości, gdzie można było zrobić "ręczne" kopiowanie po częściach.
Zalety:
Wady: