들어가며
파이썬 사용 중 리스트를 복사하고, 복사한 리스트를 수정했더니 원본 리스트까지 함께 수정되는 현상을 만난적이 있으신가요? 파이썬에 대해 깊이 공부하지 않은 상태로 리스트 복사를 시도 해 보신 분이라면 이러한 현상을 마주하는건 어찌보면 당연한 일일지도 모릅니다.
깊은 복사와 얕은 복사는 파이썬에서 데이터 구조를 복사할 때 꽤 중요한 개념입니다. 이 두 가지 개념을 이해하면 데이터 조작 시 발생할 수 있는 의도치 않은 부작용을 피할 수 있을거에요. 이번 글에서는 얕은 복사와 깊은 복사가 무엇인지. 기존의 복사와 어떠한 차이점이 있는지 알아볼게요.
리스트 복사 후 수정했는데 원본 리스트도 바뀐다…
리스트를 복사한 후, 복사본을 수정했을 때 원본 리스트도 함께 변경되는 경험을 해보신 적이 있나요? 이는 파이썬에서 기본적으로 제공하는 얕은 복사를 사용했기 때문입니다. 이 문제를 해결하기 위해서는 깊은 복사를 사용해야 합니다. 얕은 복사와 깊은 복사에 대해 더 알아보세요.
‘=’ 를 이용해 리스트 복사해 사용
등호(=)를 사용하여 변수를 다른 변수에 할당하면, 두 변수는 동일한 객체를 참조하게 됩니다. 이는 두 변수가 동일한 메모리 주소를 가리키므로, 한 변수를 통해 객체를 수정하면 다른 변수도 영향을 받습니다.
original_list = [1, 2, [3, 4]]
assigned_list = original_list
# assigned_list를 수정하면 original_list도 영향을 받습니다.
assigned_list[0] = 10
print(original_list) # [10, 2, [3, 4]]
print(assigned_list) # [10, 2, [3, 4]]
# 중첩된 리스트를 수정해도 동일한 영향을 미칩니다.
assigned_list[2][0] = 100
print(original_list) # [10, 2, [100, 4]]
print(assigned_list) # [10, 2, [100, 4]]
이러한 현상을 막기 위해 copy 패키지를 사용해 줄 거에요.
얕은 복사 (Shallow Copy)
하지만 copy에도 종류가 있습니다. 기본 copy, deepcopy로 나누어 지는데요. 먼저 얕은 복사인 copy를 한번 보겠습니다.
얕은 복사는 원본 객체의 최상위 레벨 요소만을 복사합니다. 즉, 리스트와 같은 복합 객체의 경우, 리스트 자체는 새롭게 복사되지만, 리스트 안에 있는 객체들은 원본 리스트와 동일한 참조를 가집니다.
예제
import copy
original_list = [1, 2, [3, 4]]
# 얕은 복사
shallow_copied_list = copy.copy(original_list)
# 리스트 수정
shallow_copied_list[0] = 10
print(original_list) # [1, 2, [3, 4]]
print(shallow_copied_list) # [10, 2, [3, 4]]
# 중첩된 리스트 수정
shallow_copied_list[2][0] = 30
print(original_list) # [1, 2, [30, 4]]
print(shallow_copied_list) # [10, 2, [30, 4]]
설명
shallow_copied_list[0]를 변경해도original_list는 영향을 받지 않지만,shallow_copied_list[2][0]를 변경하면original_list의 중첩된 리스트도 영향을 받습니다. 이는 중첩된 리스트가 동일한 참조를 가지고 있기 때문입니다.
그럼 어떻게 해야 이러한 현상을 막을 수 있을까요? 위에서 이미 눈치 채셨겠죠? 바로 아래 깊은 복사를 활용 해 주시면 됩니다.
깊은 복사 (Deep Copy)
깊은 복사는 원본 객체와 모든 중첩된 객체들까지 모두 재귀적으로 복사하는데요. 따라서 복사본과 원본은 완전히 독립적인 객체가 됩니다.
얕은 복사는 바탕화면에 있는 파일의 바로가기 링크를 복사해 사용하는 것이었다면, 깊은 복사는 바탕화면에 있는 파일 자체를 복사하는 것과 같습니다. 복사된 파일은 원본 파일과 동일한 내용을 가지고 있지만, 서로 독립적인 파일이므로 한 파일을 수정해도 다른 파일에는 영향을 미치지 않습니다.
예제
import copy
original_list = [1, 2, [3, 4]]
# 깊은 복사
deep_copied_list = copy.deepcopy(original_list)
# 리스트 수정
deep_copied_list[0] = 10
print(original_list) # [1, 2, [3, 4]]
print(deep_copied_list) # [10, 2, [3, 4]]
# 중첩된 리스트 수정
deep_copied_list[2][0] = 30
print(original_list) # [1, 2, [3, 4]]
print(deep_copied_list) # [10, 2, [30, 4]]
설명
deep_copied_list[0]를 변경해도original_list는 영향을 받지 않으며,deep_copied_list[2][0]를 변경해도original_list는 영향을 받지 않습니다. 이는 깊은 복사로 인해 모든 중첩된 객체들까지 복사되었기 때문입니다.
마무리
- 얕은 복사는 최상위 레벨의 객체만을 복사하며, 중첩된 객체들은 원본과 동일한 참조를 가집니다. 파이썬에서
copy.copy()함수를 사용하여 얕은 복사를 수행할 수 있습니다. - 깊은 복사는 최상위 레벨의 객체뿐만 아니라 모든 중첩된 객체들까지 재귀적으로 복사합니다. 파이썬에서
copy.deepcopy()함수를 사용하여 깊은 복사를 수행할 수 있습니다.
이 두 가지 복사 방법을 적절히 사용하면 데이터 조작 시 예상치 못한 문제를 방지할 수 있습니다. 데이터 구조의 복사 시나리오에 따라 얕은 복사와 깊은 복사 중 어떤 것을 사용할지 판단하고 적절한 방법을 선택하는 능력도 중요하겠습니다.