programowanieProgramista Backend

Jaka jest różnica między shallow a deep copy w strukturze dict oraz jak poprawnie kopiować zagnieżdżone słowniki w Pythonie?

Zdaj rozmowy kwalifikacyjne z asystentem AI Hintsage

Odpowiedź.

Historia pytania

W Pythonie struktura danych dict jest często używana do przechowywania zagnieżdżonych informacji. Programiści często spotykają się z koniecznością klonowania takich struktur podczas pracy z szablonami, konfiguracjami lub przekazywaniem danych między częściami aplikacji.

Problem

Standardowe kopiowanie dict za pomocą przypisania (=) tworzy jedynie odniesienie do oryginalnego obiektu. Powierzchowne kopiowanie (shallow copy) kopiuje sam obiekt dict, ale nie zagnieżdżone obiekty. Głębokie kopiowanie (deep copy) rekurencyjnie kopiuje wszystkie obiekty wewnątrz, co zapobiega wpływowi zmian w jednej kopii na drugą.

Rozwiązanie

Do powierzchownego kopiowania można użyć dict.copy() lub konstruktora dict(), do głębokiego — moduł copy i funkcję deepcopy():

import copy d = {"a": 1, "b": {"c": 2}} shallow_d = d.copy() deep_d = copy.deepcopy(d) # Teraz zmiana shallow_d['b']['c'] wpłynie na d['b']['c'] # zmiana deep_d['b']['c'] nie wpłynie na oryginalny słownik

Kluczowe cechy:

  • Powierzchowne kopiowanie kopiuje tylko pierwszą „powłokę” obiektu
  • Głębokie kopiowanie rekurencyjnie kopiuje wszystkie obiekty wewnątrz struktury
  • Przy pracy z zagnieżdżonymi strukturami zawsze używaj deepcopy, jeśli wymagana jest całkowita niezależność kopii

Pytania z podtekstem.

Czy dict.copy() może kopiować zagnieżdżenia głębiej niż pierwszy poziom?

Nie, dict.copy() tworzy tylko powierzchowną kopię. Zagnieżdżone słowniki oraz tak czy owak będą odniesieniami do tych samych obiektów, co w oryginalnym dict.

Czy jeśli w strukturze jest obiekt niemutowalny (np. krotka), to czy deepcopy go głęboko skopiuje?

Deepcopy kopiuje tylko zmienne zagnieżdżone obiekty. Obiekty niemutowalne pozostają takie same — krotki, ciągi i liczby nie są kopiowane rekurencyjnie, a po prostu przenoszone do kopii.

Czy do głębokiego kopiowania można używać serializacji poprzez json.loads(json.dumps(dict))?

Można, ale z zastrzeżeniami. Taki sposób działa tylko dla serializowalnych typów i nie nadaje się, jeśli w słowniku znajdują się obiekty nieserializowalne (np. funkcje lub niestandardowe klasy):

import json orig = {"a": 10, "b": [1,2,3]} copy_like_deep = json.loads(json.dumps(orig)) # Nie działa dla złożonych obiektów

Typowe błędy i antywzorce

  • Używanie prostego przypisania zamiast kopiowania
  • Stosowanie powierzchownego kopiowania do złożonych struktur, co prowadzi do nieprzewidywalnych zmian we wszystkich „kopiach”

Przykład z życia

** Negatywny przypadek Programista klonuje ustawienia za pomocą copy(), a następnie zmienia zagnieżdżoną wartość, sądząc, że to dwie niezależne struktury. Zalety: Proste i szybkie Wady: Zmiany w zagnieżdżonych obiektach w jednej kopii odzwierciedlają się we wszystkich — błędy trudne do debugowania. ** Pozytywny przypadek Programista zawsze używa copy.deepcopy() dla zagnieżdżonych struktur, nawet jeśli oryginalny dict wydaje się płaski. Zalety: Gwarantowana niezależność danych, błędy zminimalizowane Wady: Deepcopy jest wolniejszy i zużywa więcej pamięci, czasami jest nadmiarowy.