2 분 소요

얕은 복사와 깊은 복사


python에서의 복사를 알아보자.


얕은 ? 깊은 ?


python 에서 특정 객체 또는 값을 복사할 때에 얕은 복사 또는 깊은 복사 라는 표현을 종종 볼 수 있다.


얕은 복사란 쉽게 말해 메모리의 값이 아닌 주소를 복사하는 것을 의미한다.


그리고 깊은 복사는 메모리의 값을 복사하는 것을 의미한다.


코드를 통해 알아보자.


얕은 복사 예시


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

print(f'a = {a}')
print(f'b = {b}')

a[2] = 0

print('값 변경')
print(f'a = {a}')
print(f'b = {b}')


위에서 보면 a 리스트는 3개의 정수형 객체를 포함하고 있다.


그리고 b 리스트를 a 리스트로 초기화 해주었다.


이후 a의 2번 인덱스에 해당하는 ‘3’ 의 값을 ‘0’으로 변경해줬다.


얕은 복사란 개념을 처음 접하는 사람이라면 마지막 출력에 대해서

1,2,0

1,2,3

이라고 예측할 수도 있지만 결과는 그렇지 않다.


다음과 같은 실행 결과를 얻을 수 있다.

a = [1, 2, 3]
b = [1, 2, 3]
값 변경
a = [1, 2, 0]
b = [1, 2, 0]


이유는 위에서 설명한대로 b = a 를 했을 때 힙 영역에 새로운 메모리를 할당하는 것이 아닌 기존에 존재하는 1,2,3 의 주소를 참조하도록 하기 때문이다.


깊은 복사


위의 상황에서 개발자가 원하는 실행 결과를 얻기 위해선 깊은 복사가 필요하다.


깊은 복사를 하는 방법은 여러가지가 있지만 기본적인 2가지 방법을 알아보자.


슬라이싱을 이용한 깊은 복사


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

print(f'a = {a}')
print(f'b = {b}')

a[2] = 0

print('값 변경')
print(f'a = {a}')
print(f'b = {b}')


a = [1, 2, 3]
b = [1, 2, 3]
값 변경
a = [1, 2, 0]
b = [1, 2, 3]


슬라이싱을 이용할 경우 리스트의 주소가 아닌 값을 반환하는 방식으로 실행 되기 때문에 깊은 복사가 된다.


copy 메소드 사용하는 깊은 복사


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

print(f'a = {a}')
print(f'b = {b}')

a[2] = 0

print('값 변경')
print(f'a = {a}')
print(f'b = {b}')
a = [1, 2, 3]
b = [1, 2, 3]
값 변경
a = [1, 2, 0]
b = [1, 2, 3]


파이썬 내장 함수인 copy 함수를 사용함으로서 깊은 복사가 가능하다.


2차원 리스트에서의 얕은 복사


2차원 리스트에서는 이전과 같은 방식으로 깊은 복사가 되지 않는다.


a = [[1,2,3],[4,5,6],[7,8,9]]
b = a.copy()

print(f'a = {a}')
print(f'b = {b}')

a[2][2] = 0
a[1] = 0
b[0][0] = 100
print('값 변경')
print(f'a = {a}')
print(f'b = {b}')


분명 copy 메소드를 사용해 깊은 복사를 했음에도 다음과 같은 결과를 얻게 된다.


a = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
b = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
값 변경
a = [[100, 2, 3], 0, [7, 8, 0]]
b = [[100, 2, 3], [4, 5, 6], [7, 8, 0]]


해당 결과를 통해 copy 메소드를 사용하더라도 1차원 리스트는 깊은 복사가 되지만 2차원 리스트에 대해서는 얕은 복사가 이뤄진 다는 것을 알 수 있다.


2차원 리스트에서의 깊은 복사


2차원 리스트에서는 기존의 방법만으론 깊은 복사가 어렵다.


다행히 python 내장 패키지인 copy 패키지를 사용함으로서 쉽게 해결 가능하다.


a = [[1,2,3],[4,5,6],[7,8,9]]
b = copy.deepcopy(a)

print(f'a = {a}')
print(f'b = {b}')

a[2][2] = 0
a[1] = 0
b[0][0] = 100
print('값 변경')
print(f'a = {a}')
print(f'b = {b}')
a = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
b = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
값 변경
a = [[1, 2, 3], 0, [7, 8, 0]]
b = [[100, 2, 3], [4, 5, 6], [7, 8, 9]]


짜잔 copy 패키지의 deepcopy 메소드를 사용함으로서 다차원 리스트에 대한 깊은 복사가 가능하다.


Why ?


왜 python이나 다른 언어들은 깊은 복사와 얕은 복사를 나눠놔서 개발자들을 헷갈리게 하는 걸까 ?


이유는 메모리를 효율적으로 사용하기 위함이다.


최대한 데이터의 중복을 없앰으로서 컴퓨터가 가진 리소스를 효율적으로 사용하는 것이다.


완전히 중복된 데이터라면 굳이 같은 값에 대해서 여러 메모리를 할당하는 것은 낭비이다.

댓글남기기