2 분 소요

1. 단순 객체복제

변수만 복사하다 보니 바라보는 객체는 당연히 동일하겠죠. 즉, 두개의 변수 중 하나만 변경되어도 나머지 하나도 동일하게 수정되는 현상이 발생하게 됩니다.

a = [1, 2, 3, 4]
b = a     # shallow copy
print(b)    # [1, 2, 3, 4]
b[2] = 100   # b의 item 변경
print(b)    # [1, 2, 100, 4]
print(a)    # [1, 2, 100, 4], a의 item도 수정됨!!

리스트를 만들어(파이썬에서 대괄호’[ ]’는 list) 1 에서 4 까지 네개의 숫자를 넣고 a 에 할당했습니다. 그러면 a 는 리스트 객체의 주소를 바라보는 변수가 되는 것이죠. 그런뒤 a 를 b 에 할당해 주었습니다. 그러면 b 는 a 와 같은 객체의 주소를 바라보게 됩니다.

문제는 a 또는 b 를 수정하게 되면 문제가 발생합니다. 위에서 처럼 b 의 세번째(2) 값을 3 에서 100 으로 바꿔주게 되면, 당연히 b 는 [1, 2, 100, 4] 처럼 출력됩니다. 그런데 변경되지 않을 거라고 예상했던 a 또한 b 와 같이 [1, 2, 100, 4] 로 수정되어버렸습니다. 이는 a 와 b 가 동일한 객체를 참조하기 때문에 발생하는 문제입니다.

한가지 주의할 점은 위의 경우처럼 복사된 참조 변수를 수정했을때, 처음에 할당한 참조 변수의 값 역시 똑같이 수정되는 것은 리스트와 같은 변경가능(mutable) 객체일 때만 해당한다는 것입니다. 숫자나 문자열과 같은 불변의(immutable) 객체일때는 위의 경우가 해당되지 않습니다.

a = 10
b = a
print(b)    # 10 출력력
b = "abc"
print(b)    # abc 출력
print(a)    # 10 출력

위와 같은 결과가 나타나는 이유는, 불변의 객체이기 때문입니다. 말장난 하는것 같아 보이지만, 불변의 객체란 값이 바뀌지 않는 객체를 뜻하죠. 그렇기 때문에 참조변수를 수정한다는 것은 같은 주소의 값(value)이 바뀌는 것이 아니라 그 변수에 새로운 객체가 할당되는 것을 뜻합니다.

2. 얕은 복사(shallow copy)

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

import copy

a = [1, [1, 2, 3]]
b = copy.copy(a)    # shallow copy 발생
print(b)    # [1, [1, 2, 3]] 출력
b[0] = 100
print(b)    # [100, [1, 2, 3]] 출력,
print(a)    # [1, [1, 2, 3]] 출력, shallow copy 가 발생해 복사된 리스트는 별도의 객체이므로 item을 수정하면 복사본만 수정된다. (immutable 객체의 경우)

c = copy.copy(a)
c[1].append(4)    # 리스트의 두번째 item(내부리스트)에 4를 추가
print(c)    # [1, [1, 2, 3, 4]] 출력
print(a)    # [1, [1, 2, 3, 4]] 출력, a가 c와 똑같이 수정된 이유는 리스트의 item 내부의 객체는 동일한 객체이므로 mutable한 리스트를 수정할때는 둘다 값이 변경됨

리스트 내에 리스트가 있는 경우에 얕은 복사(b = copy.copy(a))가 이뤄지더라도 리스트 내의 내부 리스트까지 별도의 객체로 복사가 되는것은 아닙니다.

위의 예제에서 b 에서 첫번째 요소(숫자)를 변경하였을때 a 가 변경되지 않은 것은 그 요소가 immutable 하기 때문입니다. immutable 하다는 것은, 요소가 수정되는 것이 아니라 그저 다른 값으로 대체된다고 볼 수 있죠. 그렇기 때문에 b 에서 변경된 요소가 a 에는 반영되어 있지 않은 것이죠.(두 리스트는 다른 객체이므로)

그러나 c 의 경우는 좀 다릅니다. a 를 복사하여(c = copy.copy(a)) c 를 만듭니다. 그리고 c 의 두번째 요소(리스트)에 새로운 값을 추가합니다. 출력해보면 a 와 b 의 경우처럼 c 에 값을 수정했을때 a 는 수정되지 않을거라 기대되지만, 실제는 그렇지 않습니다. c 의 내부리스트를 수정하게 되면 a 의 내부리스트 또한 바뀌게 되는데요. 그 이유는 a 와 c 의 내부리스트는 같은 객체를 참조하기 때문입니다. b 의 경우에도 같은 객체라 말할 수도 있지만, 이 둘의 중요한 차이는 그 객체가 mutable 하냐 immutable 하냐의 차이입니다. mutable 한 경우에는 값이 수정될수 있지만, immutable 한 경우에는 값이 수정되는 것이 아니라 아예 새로운 객체로 변경되는 것이죠. 그래서 위와 같은 차이가 나타나게 되는 것입니다.

3. 깊은 복사(deep copy)

mutable 한 내부객체(내부리스트)의 문제를 해결하기 위해서는 얕은 복사가 아닌 깊은 복사(deep copy)를 해야 합니다.

얕은 복사가 복합객체(리스트)만 복사되고 그 안의 내용은 동일한 객체를 참조한다면, 깊은 복사의 경우에는 복합객체를 새롭게 생성하고 그 안의 내용까지 재귀적으로 새롭게 생성하게 됩니다.

그래서 깊은 복사를 하게 되면, 처음에 만들었던 객체와 복사된 객체가 전혀 달라지기 때문에 어느 한쪽을 수정한다고 해서 다른 한쪽이 영향 받는 일은 없게되겠죠.

import copy

a = [1, [1, 2, 3]]
b = copy.deepcopy(a)    # deep copy 실행
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]] 출력

정리해보면,

  1. 단순복제는 완전히 동일한 객체,
  2. 얕은복사(shallow copy)는 복합객체(껍데기)만 복사, 그 내용은 동일한 객체
  3. 깊은복사(deep copy)는 복합객체 복사 + 그 내용도 재귀적으로 복사

태그:

카테고리:

업데이트:

댓글남기기