[Python] 얕은복사와 깊은 복사

채린·2023년 3월 21일
1

Python

목록 보기
1/1
post-thumbnail

💗 Python 에서의 복사 방법

1) 할당(Assignment)
2) 얕은 복사(Shallow copy)
3) 깊은 복사(Deep copy)

얕은 복사에 할당도 포함됨
복사 방법을 알기 전에 mutable객체와 immutable 객체의 차이에 대해 알아보자

👀 mutable, immutable 객체

구분타입
Immutable(수정 불가능한 객체)int, float, str, tuple
Mutable(수정 가능한 객체)list, dict

(1) Immutable 객체 예시(int)

a = 1 
print(hex(id(a))) # 0x7fae9621b7a0 
b = 1 
print(hex(id(a))) # 0x7fae9621b7a0 

print(a == b) # True 

a += 1
print(hex(id(a))) # 0x7fae9621b7c0 

a 와 b 를 따로 생성했으나 같은 주소를 가리키는 걸 볼 수 있음, a 값이 변경되니 참조하는 주소가 바뀌었음

  • 참고: 단, 같은 문자라고 해서 같은 주소를 참조하는 것만은 아님. 하지만 같은 문자면 거의 대부분 같은 값을 참조한다는 것은 사실임
a = 'HIHI' 
b = 'HIHI' 
print(hex(id(a)), hex(id(b))))
# 0x7f0b17fd6c70 0x7f0b17fd6c70

a = a.replace('HI', 'JI') 
b = 'JIJI' 
print(hex(id(a)), hex(id(b)))) 
# 0x7f0b0caf7d50 0x7f0b17fe9e30 

(2) mutable 객체 예시 (list)

a = [1, 2, 'a'] 
b = [1, 2, 'a']
print(hex(id(a)), hex(id(b))) 
# 0x7fcf5b8fbd48 0x7fcf5c3b4408  -> 같음 
a.append(10)
b.append(10)
print(hex(id(a)), hex(id(b)))  
# 0x7fcf5b8fbd48 0x7fcf5c3b4408 -> 같음 

  • 즉, 값은 바뀌었는데 주소는 변하지 않은 것을 알 수 있음
a = [1, 2, [1, 2]] 
b = [1, 2, [1, 2]] 

print(hex(id(a[0])), hex(id(b[0])))
# 0x7f1191d867a0 0x7f1191d867a0 (같음) 
print(hex(id(a[1])), hex(id(b[1]))) 
# 0x7f1191d867c0 0x7f1191d867c0 (같음) 
print(hex(id(a[2])), hex(id(b[2]))) 
# 0x7f1185f8fcc8 0x7f1187393d08 (다름) 

즉, 배열 안에 있는 각각의 immutablle 요소는 같은 주소를 가지지만, mutable 요소는 다른 주소를 가짐

👀 할당 (Assignment)

  • 대입 연산자(=) 를 통한 복사

(1) immutable 객체 할당 (int)

a = 30 
b = a 

b += 1 
print(a, b) # 30, 31  

-> 같은 주소를 가리키며, 값을 변경했을 때 다른 주소를 가리킴 (각기 다른 참조)

(2) mutable 객체 할당 (list)

original_list = [1, 2, 3] 
copy_list = original_list
print(original_list, copy_list) 
# [1, 2, 3] [1, 2, 3] 

copy_list[0] = 1000
print(original_list, copy_list) 
# [1000, 2, 3] [1000, 2, 3] 

같은 주소를 가리키며, 값이 변경되어도 참조하는 주소가 동일함

즉, 해당 주소의 일부 값을 변경하는 경우 이를 참조하는 모든 변수에 영향을 줌

👀 얕은 복사 (Shallow copy)

  • Slice 연산자(:) 또는 copy 를 import 해서 사용

(1) slice 연산자 사용

original_list = [1, 2, 3] 
copy_list = original_list[:]
print(original_list, copy_list) 
# [1, 2, 3] [1, 2, 3] 

copy_list[0] = 1000
print(original_list, copy_list)
# [1, 2, 3] [1000, 2, 3] 

할당과는 다르게 다른 주소에 연산된 결과를 복사

하지만, 리스트안에 있는 리스트 or 딕셔너리 등은 똑같이 복사하지 못함

original_list = [1, 2, [1, 2]] 
copy_list = original_list[:]
print(original_list, copy_list)
# [1, 2, [1, 2]] [1, 2, [1, 2]] 

copy_list[0] = 1000
print(original_list, copy_list) 
# [1, 2, [1, 2]] [1000, 2, [1, 2]] 

copy_list[2][0] = 2000
print(original_list, copy_list)
# [1, 2, [2000, 2]] [1000, 2, [2000, 2]] 

  • 딕셔너리도 같음
original_list = [1, 2, {'name': 'chaelin', 'id': 1}]  
copy_list = original_list[:]
print(original_list, copy_list) 
# [1, 2, {'name': 'chaelin', 'id': 1}] [1, 2, {'name': 'chaelin', 'id': 1}]

copy_list[2].update(name='chaechae')
print(original_list, copy_list)
# [1, 2, {'name': 'chaechae', 'id': 1}] [1, 2, {'name': 'chaechae', 'id': 1}] 

(2) import copy 사용

import copy 

original_list = [1, 2, [3, 4]] 
copy_list = copy.copy(original_list) 
print(original_list, copy_list) 
# [1, 2, [3, 4]] [1, 2, [3, 4]] 
copy_list[0] = 1000
print(original_list, copy_list)
# [1, 2, [3, 4]] [1000, 2, [3, 4]] 
copy_list[2][0] = 2000
print(original_list, copy_list) 
# [1, 2, [2000, 4]] [1000, 2, [2000, 4]] 

  • import copy를 하지 않고 original_list.copy() 하는 방법도 있음

👀 깊은 복사(Deep copy)

  • 완전하게 복사하고 싶을 때 사용
import copy 

original_list = [1, 2, [3, 4]] 
copy_list = copy.deepcopy(original_list) 
print(original_list, copy_list)  
# [1, 2, [3, 4]] [1, 2, [3, 4]] 
copy_list[0] = 1000
print(original_list, copy_list)
# [1, 2, [3, 4]] [1000, 2, [3, 4]] 
copy_list[2][0] = 2000
print(original_list, copy_list)
# [1, 2, [3, 4]] [1000, 2, [2000, 4]] 

📚 참고

Image by Ylanite Koppens from Pixabay

0개의 댓글