개발자 블로그

[윤성우의 열혈 파이썬 중급편] - 3. 깊은 복사와 얕은 복사 본문

파이썬/윤성우의 열혈 파이썬 중급편

[윤성우의 열혈 파이썬 중급편] - 3. 깊은 복사와 얕은 복사

hayongwoon 2022. 4. 26. 01:15

두 객체의 비교와 복사

객체를 비교할 때 사용할 수 있는 두 가지 연산자 '==', 'is' 얼핏 보면 같아보이지만, 사실 이 두 가지의 차이를 분명하게 짚고 넘어가야한다.

 

 v1 == v2               변수 v1과 v2가 참조하는 객체의 내용이 같은가?
 v1 is v2               변수 v1과 v2가 참조하는 객체는 동일한 객체인가?

 

예시를 보며 설명해보자.

>>> r1 = [1,2,3]
>>> r2 = [1,2,3]

>>> r1 is r2 #참조하는 객체가 동일 객체인가? 즉, 메모리 주소가 같니?
False
>>> r1 == r2 #참조하는 객체의 내용이 같은가? 즉, 두 값의 내용이 일치하니??
True

참조에 대해 앞서 블로깅을 했지만 여기서 간략히 짚고 넘어가자면, 메모리에 저장된 값에 변수명이 적힌 포스트잇을 붙이는 것이라고 이해를 했다. 때문에 is 연산은 참조 즉, 가르키는 객체가 같은지를 묻는 것이고 == 연산은 안에 내용만을 같은지 묻는 것이라 할 수 있다. 

 사실 같은 것을 참조하면 안에 내용은 같을 수 밖에 없지 않은가? 그렇다면 is 연산이 == 연산보다 더 세밀한 연산이겠네? 라는 생각이 든다.

 

본론으로 들어가서 객체의 복사에 대해 살펴보자!

 사실 복사라고 하면 같은 값을 다른 변수에 할당함으로써 복사가 이루어진다고 볼 수 있겠다. 하지만 여기서 문제가 생길 수 있다. 두 변수가 같은 객체를 참조하고 있을 때, 참조하는 객체가 수정 가능하다면 즉, mutable 객체이면 문제가 생길 수도 있다는 것이다. 

 그렇다면 왜? 무슨 문제가 생기는 지 예제를 보며 알아보자!

>>> r1 = ['yongwoon', ('man', 'KOREA'), [181, 83]]
>>> r2 = list(r1) #r1과 같은 내용으로 새로운 리스트를 만들어 줬다.
>>> r1 is r2
False
>>> r1[0] is r2[0] # r2와 r1이 'yongwoon'이라는 객체는 동일한 객체를 참조하고 있다.
True
>>> r1[1] is r2[1] # ('man', 'KOREA')마찬가지로 동일한 객체를 참조
True
>>> r1[2] is r2[2] # [181, 83] 동일한 객체를 참조
True

1) r1과 r2, 값은 같지만 새로운 리스트를 만들어 줌으로써 두 개의 리스트를 생성

 

2) 두 리스트 안에 객체들은 복사가 되었기 때문에, 참조하는 객체가 동일!

 

여기서 문제는 2)에서 발생할 수 있다는 것이다. 두 개의 리스트가 존재하지만 그 안에 객체는 복사를 통해 동일하기 때문이다. 만약, 한개의 리스트 안에 객체를 수정하고 싶다고 가정하자. 과연 변경하고픈 리스트의 객체만 값이 바뀌었을까? 

예시를 통해 살펴보자 

##얕은 복사 예제###
>>> EX2021 = ['yongwoon', ('man', 'KOREA'), [181, 28]]
>>> EX2022 = list(EX2021) # 새로운 리스트를 생성하여 복사!!
>>> EX2022[2][1] += 1 # 2022년에는 한 살 더 먹었으니 업데이트를 해줘야겠다.

#나의 기댓값
>>> EX2022
['yongwoon', ('man', 'KOREA'), [181, 29]] # 잘 나왔네 ~~ 넘어가자 ^^ 하지만...
>>> EX2021
['yongwoon', ('man', 'KOREA'), [181, 29]] # 복사를 통해 같은 객체를 참조하고 있어 같이 값이 바뀌었다...

예시 코드를 보면 알겠지만, 복사를 통해 같은 객체를 참조하고 있어 EX2021의 리스트 값도 바뀐 것을 볼 수 있다. 아니 정확히 말하면 같은 객체를 바로보고 있기 때문에 EX2021의 리스트 값이 바뀐게 아니라 그냥 둘이 바라보고 있는 동일한 객체의 값이 바뀐거다.

 

그렇다면 이러한 문제는 어떻게 해결할 것인가? 해결책은 깊은 복사에 있다.

python에서는 이러한 문제를 해결하기 위해 copy모듈의 deepcopy 함수를 사용하면 된다. 이 함수는 객체를 복사를 해올 때, mutable 객체는 깊은복사 즉, 같은 값의 새로운 객체를 생성하여 참조를 달리 해주게 하고, immutable 객체에 대해서는 얕은 복사 즉, 동일한 객체를 복사해온다. deepcopy 함수를 사용하면 안전성과 성능을 모두 만족시킬 수 있다.

 

###깊은 복사 예제###
>>> EX2021 = ['yongwoon', ('man', 'KOREA'), [181, 28]]
>>> import copy
>>> EX2022 = copy.deepcopy(EX2021) 
>>> EX2022[2][1] += 1 
>>> EX2022
['yongwoon', ('man', 'KOREA'), [181, 29]] 
>>> EX2021
['yongwoon', ('man', 'KOREA'), [181, 28]]
>>> (EX2021[0] is EX2022[0]) and (EX2021[1] is EX2022[1])
True
>>> EX2021[2] is EX2022[2] #리스트에 대해서는 깊은 복사가 이루어짐. 내용은 같지만 같은 객체를 참조하고 있는 것은 아님.
False

 

문자열과 튜플과 같이 immutable한 객체에 대해서는 얕은 복사가 이루어졌고, mutable(리스트) 객체에 대해서는 깊은 복사를 통해 참조한 객체가 다른 것을 확인할 수 있었다.