[파이썬]얕은 복사와 깊은 복사

데이터를 복사하는 방법에는 크게 얕은 복사깊은 복사가 있다. 이 둘에 대해서 어떠한 차이점이 있는지 알아보고 흔히 할 수 있는 실수에 대해서 정리해보자.

기본 개념

파이썬에서의 대입문

파이썬에서 대입문은 객체를 복사하지 않고 대상과 객체 사이에 바인딩을 만든다.

얕은 복사

새로운 객체를 생성한 후 원본에 접근할 수 있는 참조를 입력한다.

깊은 복사

새로운 객체를 생성한 후 원본의 복사본으로 데이터를 채워 넣는다.

코드로 비교를 해 보면 더 차이점이 와닿을 것 같다.

대입문을 통한 복사

1
2
3
4
5
6
7
8
a = [1, 2, 3]
b = a

id(a), id(b) # id값이 같다

b[0] = 'aaa'
a # ['aaa', 2, 3]
b # ['aaa', 2, 3]

이미 잘 알려져있는 예제이다. 위의 파이썬의 대입문에서 말했듯이 객체를 복사하지 않고 바인딩을 만들기 때문에 a와 b 변수는 같은 리스트 객체를 참조하고 있다. 따라서 둘 중 하나만 변경이 되더라도 모든 변수의 데이터에 영향을 주게 된다.

위의 문제를 해결하기 위해서 보통은 아래와 같은 방법을 사용해 복사를 수행한다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
from copy import copy

a = [1, 2, 3]
b = a[:]
c = list(a)
d = copy(a)

id(a), id(b), id(c),id(d)
# (4590448328, 4592590408, 4592444552, 4590818120)

a[0] = 'aaa'
a # ['aaa', 2, 3]
b # [1, 2, 3]
c # [1, 2, 3]
d # [1, 2, 3]

리스트 슬라이스 / list 함수 / copy 함수를 통해 복사하면 객체의 id값이 다르기 때문에 다른 데이터에는 영향을 주지 않게 된다. 그리고 이러한 복사 방식을 얕은 복사라고 부른다. 얼핏보면 문제없어 보이지만 리스트 안에 리스트가 있을 경우에는 예기치 못한 상황이 생긴다.

얕은 복사(copy 함수)

1
2
3
4
5
6
7
8
9
a = [1, [1, 2, 3]]
b = copy(a) # 얕은 복사 수행

id(a), id(b)
# (4592590728, 4592373128)

a[1].append('aa')
a # [1, [1, 2, 3, 'aa']]
b # [1, [1, 2, 3, 'aa']]

copy 모듈의 copy 함수를 통해 변수 b를 만들고 리스트안의 리스트를 수정했더니 a, b 두 변수의 값이 변경되었다. 즉, copy 함수도 얕은 복사를 수행한다는 것이다. 원본의 값이 바뀌어도 복사된 객체에 영향을 주지 않으려면 깊은 복사(deepcopy)가 필요하다.

깊은 복사(deepcopy)

1
2
3
4
5
6
7
8
9
10
11
from copy import deepcopy

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

id(a), id(b)
# (4592642568, 4591296456)

a[1].append('aa')
a # [1, [2, 3, 4, 'aa']]
b # [1, [2, 3, 4]]

이렇게 깊은 복사를 통해 복사를 하면 원본 데이터의 값이 변경되어도 복사본은 영향을 받지 않는다.

얕은 복사와 깊은 복사의 차이

어떠한 차이점이 있는지 정리해보자.

1
2
3
4
5
6
7
8
9
10
11
12
13
# 얕은 복사
a = [1, [1, 2, 3]]
b = copy(a)
c = deepcopy(a)

id(a), id(b), id(c)
# (4592854280, 4591051528, 4594252744)
# 일단 모든 객체는 서로 다른 주소를 가진다

id(a[1]), id(b[1]), id(c[1])
# (4592595592, 4592595592, 4594187592)
# 얕은 복사한 b의 내부 리스트는 원본과 같은 주소를 가진다
# 그렇기 때문에 원본에 영향을 받는 것이다

얕은 복사과 깊은 복사 모두 객체를 복사하긴한다. 하지만 얕은 복사의 경우 저장된 데이터들까지 복사를 하진 않기때문에 서로가 영향을 주고 받으며, 완전히 독립적인 객체로 만들기 위해서는 깊은 복사가 필요한 것이다.

마지막으로 정리를 해보면,

  • 대입문을 통한 복사는 단순 복제(동일한 객체를 참조)
  • 얕은 복사는 껍데기만 복사, 내용은 동일한 객체를 참조
  • 깊은 복사는 껍데기를 복사하고 내용도 재귀적으로 복사

[참고]
https://docs.python.org/ko/3/library/copy.html
https://kongdols-room.tistory.com/67

Share