얕은 복사(shallow copy)와 깊은 복사(deep copy)

hs·2021년 8월 20일
3
post-thumbnail

❓ 의문

다른 분의 코딩을 보다 이게 왜 돌아가지했던 부분이 생겨 찾아보다 알게된 것이다.
일단 의문이 생긴 코딩을 보자.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
actors = Actor.objects.all()
        results = []
        for actor in actors:
            title_list = []
            results.append(
                {
                    "first_name": actor.first_name,
                    "last_name": actor.last_name,
                    "title": title_list,
                }
            )
        titles = actor.movies.all()
        for title in titles:
            title_list.append(
                {
                    "title": title.title
                }
            )
cs

여기서 반복문 처음에 title_list = []로 초기화를 주었는데 출력값이 정상적으로 나왔다. 당연히 빈 리스트가 반복적으로 나올꺼라 생각했지만 어떻게 results.append()안의 title_list에 아래쪽의 .append()의 값이 들어 갈 수 있을까?

이에 대한 정답은 얕은 복사와 깊은 복사에서 찾을 수 있었다.

🧩 원인

이러한 코드가 가능한데에는 python에서 복사를 할때 그 값을 어떻게 저장하느냐를 보면 알 수 있다.

기존에 생각하던 복사는 단순 객체 복사이다.
예시로 알아보자

a_list = [1,2,3,4]
b_list = a_list
print(b)   # [1,2,3,4]

b_list[1] = 5
print(b_list)   # [5,2,3,4]
print(a_list)   # [5,2,3,4]

분명 b_list[1]만 값을 변경했는데 a_list까지 값이 변경되었다.
그 이유는 b_list = a_list라는 코드가 복사를 시킨다는 것이 아닌 "같은 객체의 주소를 바라보게 한다." 그렇기 때문에 b_list를 수정해도 a_list도 변경된 것이다. C언어에서 포인터(*)와 같이 생각하면 된다.(주소값을 참조하여 가져온다.)

이런 부분들에 대한 객체에는 mutable과 immutable객체가 있다.

mutable ➡️ list, set, dict, user가 만든 class
immutable ➡️ bool, int, float, tuple, str, frozenset

변경가능한(mutable) 객체일때만 해당하고 불변의(immutable) 객체의 경우는 해당하지 않는다.

🧩 얕은 복사

단순 객체 복사와 얕은 복사의 차이는 복합객체(리스트)는 별도로 생성하지만 그 안에 들어가는 요소들은 원래와 같은 객체라는 점이다.

import copy

a_list = [1, [1, 2, 3]]
b_list = copy.copy(a_list)
print(b)   # [1, [1, 2, 3]]
b[0] = 20
print(b)   # [20, [1, 2, 3]]
print(a)   # [1, [1, 2, 3]]

c_list = copy.copy(a)
c[1].append(4)
print(c)   # [1, [1, 2, 3, 4]]
print(a)   # [1, [1, 2, 3, 4]]

b_list의 경우는 a_list를 복제해서 값을 넣어 준 것임으로 immutable하게 값을 넣어 준 것이다. 하지만 하지만 두번째 경우에는 복사한 c_list의 요소가 아닌 내부리스트에 .append()를 해준 것이기 때문에 내부리스트의 경우는 mutable하기 때문에 값이 같이 변화한 것을 알 수 있다. 즉, 얕은 복사의 경우 전체적인 복합객체(리스트 전체)만 복사되고 그 안의 내용은 동일하게 객체를 참조한다.

🧩 깊은 복사

깊은 복사의 경우는 복합 객체를 새로 생성하고 그 안의 내용까지 재귀적으로 새롭게 생성한다. 참조 관계가 아니기 때문에 어느 쪽을 수정하던지 반대쪽에 영향을 끼치지 않는다.

import copy

a = [1, [1, 2, 3]]
b = copy.deepcopy(a)
print(b)    # [1, [1, 2, 3]]
b[0] = 100
b[1].append(4)
print(b)    # [100, [1, 2, 3, 4]]
print(a)    # [1, [1, 2, 3]]
profile
무엇이든 끝까지 보람차게

0개의 댓글