1 minute read

본 포스팅은 “윤성우의 열혈 파이썬 중급편” 책 내용을 기반으로 작성되었습니다. 잘못된 내용이 있을 경우 지적해 주시면 감사드리겠습니다.

3-1. 두 객체의 비교와 복사

객체를 비교할 때, 헷갈리는 두 가지 유형의 연산자가 있다.

‘v1 == v2’ vs ‘v1 is v2’

v1 = [3, 6, 9]
v2 = [3, 6, 9]
v1 == v2
(결과) True
v1 is v2
(결과) False

결과부터 말하자면 다음과 같다.

‘v1 == v2’ → 변수 v1과 v2가 참조하는 객체의 내용이 같은가?
‘v1 is v2’ → 변수 v1과 v2가 참조하는 객체는 동일 객체인가? 즉, 동일한 메모리 주소의 값을 참조하는가?

3-2. 얕은 복사

다음 코드를 보자

v1 = ['막걸리', ('Wine', 'Beer'), [62, 31]]
v2 = list(v1)
v1 is v2
(결과) False

이 결과는 이미 예상하고 있었다(이전에 포스팅 한적이 있으므로 후훟…!). 하지만 각 요소값을 각각 비교한다면?

v1[0] is v2[0]
v1[1] is v2[1]
v1[2] is v2[2]
(결과) True
       True
       True

예상치 못한 결과이다!(테스트 해보면서 나도 놀람)
어째서 이런 결과가 나온걸까? 각 변수가 참조하는 값의 메모리 주소를 보자.

id(v1)
id(v2)
(결과) 2278498696392
       2278502193416
print(id(v1[0]), id(v1[1]), id(v1[2]))
print(id(v2[0]), id(v2[1]), id(v2[2]))
(결과) 2278500601456, 2278501788040 2278498696840
       2278500601456, 2278501788040 2278498696840

보시다시피 각 리스트 요소를 참조하는 메모리 주소값이 v1, v2 모두 동일하다. 리스트를 새로 생성할 때 리스트 안에 선언되는 값들이 새롭게 리스트 안에 쏙 들어가는 형태가 아니라, 각 요소 값을 리스트 안에서 참조하는 형태이기 때문이다.

이와 같은 복사 형태를 ‘얕은 복사(Shallow copy)’ 라고 한다.
‘막걸리’ 와 (‘Wine’, ‘Beer’)는 사실 변경이 불가능한 Immutable 객체이기 때문에 얕은 복사를 해도 문제가 되지는 않는다.
왜? ‘막걸리’를 ‘복분자주’로 변경한다 한들, Immutable한 객체이므로 새로운 메모리 주소에 ‘복분자주’가 들어갈 것이기 때문이다.

하지만 [62, 31]은 Mutable 객체 이므로, 이 요소를 바꿀 경우 다른 요소도 바뀌게 된다는 문제가 발생하게 된다. 나는 v1의 62 값만 5252로 바꾸고 싶은데, v2의 62 값도 5252로 바뀔 것이기 때문이다.

3-3. 깊은 복사

상기 문제를 해결하고 싶다면, copy 모듈의 deepcopy 함수를 사용하자!

import copy
v1 = ['막걸리', ('Wine', 'Beer'), [62, 31]]
v2 = list(v1)
v3 = copy.deepcopy(v1)
v1[2][0] = 5252
print(v1[2][0], v2[2][0], v3[2][0])
print(id(v1[2][0]), id(v2[2][0]), id(v3[2][0]))
(결과) 5252 5252 62
       2218787620880 2218787620880 140725770218032

이제 좋은 거 알았으니, 더 이상 메모리 가지고 장난질 하는 파이썬의 손아귀에 놀아나지 말자!

Leave a comment