본문 바로가기
Python/기초

[파이썬/Python] 얕은 복사(Shallow copy)와 깊은 복사(deep copy)에 대한 완벽 정리

by 모두의 케빈 2023. 7. 5.

얕은 복사와 깊은 복사에 대한 정의


mutable 객체, 그것이 문제로다.

 

파이썬에서는 immutable 객체와 mutable 객체가 있습니다. immutable 객체는 값을 바꿀 수 없는 객체입니다. 값이 바뀌면 다른 메모리 공간을 할당하여 주소값도 바꿔줘야 합니다. 반면, mutable 객체는 주소값이 동일하더라도 그 안의 값을 바꿀 수 있는 객체입니다.

파이썬에서 흔히 사용되는 int, str, float, boolean, tuple의 자료 유형이 immutable 객체에 포함됩니다. 반면 list, dict, set은 mutable 객체에 포함됩니다. 이 개념에 대해 잘 모르신 분들은 이전 글을 꼭 읽어봐 주세요.

얕은 복사와 깊은 복사는 mutable 객체를 복사할 때만 신경 써주시면 됩니다.

 

얕은 복사

얕은 복사는 객체의 참조값, 흔히 말하는 주소값만 복사하는 것을 의미합니다. 객체의 주소값을 복사하기 때문에, 복사 대상의 값이 바뀌면 복사한 값도 바뀌게 됩니다. 결과적으로 두 변수 간 독립성이 성립하지 않게 됩니다.

 

아래 코드를 볼까요. mutable 객체인 list를 a라고 선언했습니다. 그리고 a를 b로 복사했습니다. 파이썬은 기본적으로 객체 지향 언어여서 얕은 복사를 지향합니다. 따라서 'a=b'는 얕은 복사이며, a와 b의 주소값이 같은 것을 볼 수 있습니다. 물론 주소값이 동일하기 때문에 값도 동일합니다.

a = [1,2,3]
b = a

print(a, b)
#[1, 2, 3] [1, 2, 3]

print(id(a), id(b)) 
#2556294001920 2556294001920

 

a의 값을 바꿔 볼까요. a의 값을 바꿨더니 b의 값도 바뀐 것을 확인할 수 있습니다. 이는 a와 b 사이에 얕은 복사 관계가 성립되고, b는 a와 같은 주소를 참고하고 있기 때문에 b의 값도 바뀐 것입니다. 따라서 a와 b 두 변수 사이에는 독립성이 성립할 수 없습니다.

a[0] = 9999

print(a, b)
#[9999, 2, 3] [9999, 2, 3]

print(id(a), id(b)) 
#2556294001920 2556294001920

 

이번에는 immutable 객체인 int 형에 대해 살펴보겠습니다.

int_a = 10
int_b = int_a

print(int_a, int_b)
# 10, 10

print(id(int_a), id(int_b))
# 140707498305600 140707498305600

 

int_a의 값을 바꿔도, int_b의 값은 바뀌지 않은 것을 확인할 수 있습니다. 그 이유는 int가 immutable 객체이기 때문에, 정의에 따라 int_a의 값이 바뀌는 순간 주소값도 자연스럽게 바뀌기 때문입니다. 따라서 두 변수는 독립성을 유지할 수 있습니다.

int_a += 5

print(int_a, int_b)
# 15 10
print(id(int_a), id(int_b))
# 140707498305760 140707498305600

 

결과적으로 immutable 객체는 자동으로 변수의 독립성을 보장해 주므로, mutable 객체에 대해서만 얕은 복사와 깊은 복사를 신경 쓰면 된다는 것을 알 수 있습니다.

 

깊은 복사

깊은 복사는 다른 메모리 공간에 값을 복사함으로써, mutable 객체의 독립성을 유지해 주는 역할을 합니다. copy 모듈의 deepcopy 함수를 사용하여 깊은 복사를 할 수 있습니다. a를 b에 깊은 복사하는 순간, a와 b의 주소값이 달라지는 것을 확인할 수 있습니다. 

import copy

a = [1,2,3]
b = copy.deepcopy(a)

print(a,b)
# [1, 2, 3] [1, 2, 3]

print(id(a), id(b))
# 2556293780608 2556294024896

 

a와 b의 주소값이 변경되었기 때문에 a의 값을 바꿔도 b의 값은 유지가 됩니다. 두 변수 사이의 독립성이 확보된 셈입니다.

a[0] = 9999

print(a,b)
# [9999, 2, 3] [1, 2, 3]

print(id(a), id(b))
# 2556293780608 2556294024896

 

 

왜 얕은 복사와 깊은 복사를 알아야 할까?


 

코딩을 하다 보면, 원본의 값을 바꾸더라도 복사한 객체의 값이 바뀌지 않아야 하는 경우가 꽤 있습니다. 왜냐면 mutable 객체인 list, dict, set 같은 자료형은 정말 많이 사용되기 때문입니다.

기본적으로 얕은 복사를 하든, 깊은 복사를 하든 코드에서는 에러가 나오지 않기 때문에 나중에 최종 결과값을 출력했을 때 의도치 않은 값이 나온다면 이를 디버깅하는데 정말 고생하실 수 있습니다. 복사에 대한 개념이 없다면 말이죠.

 

기본적으로 프로그래머는 자신의 코딩을 제어할 수 있어야 하기 때문에, 이런 기초를 튼튼하게 다지신다면 나중에 도움이 될 것이라고 생각합니다.

 

댓글