내가 그랬듯이 파이썬 사용자들이 자주 헷갈려하는 리스트 복사에 대한 내용을 정리했습니다.
파이썬의 리스트 복사는 4가지 경우가 있습니다.
- 데이터를 가리키는 메모리 주소를 복사하는 경우
- 데이터 자체를 복사하는 경우
- mutable 데이터의 메모리 주소를 복사하는 경우
- mutable 데이터 자체를 복사하는 경우
보기쉽게 코드로 먼저 확인해보겠습니다.
[1번 코드]
origin_data = [1, 2, 3, 4, 5]
copy_data = origin_data
# id()는 파이썬이 객체를 구별하기 위해 부여하는 코드값
# 동일 객체 여부를 판별할 때 사용
print(f'origin_data: {origin_data} / id: {id(origin_data)}')
print(f'copy_data: {copy_data} / id: {id(copy_data)}')
결과를 보면 id값이 동일한 것을 보아 같은 데이터라고 볼 수 있습니다.
그리고
copy_data
의 데이터를 변경해봤습니다.
copy_data[0] = 10
print(f'origin_data: {origin_data} / id: {id(origin_data)}')
print(f'copy_data: {copy_data} / id: {id(copy_data)}')
origin_data
의 데이터 역시 변경된 것을 확인할 수 있습니다.
이것으로 copy_data
변수는 origin_data
의 메모리 주소를 복사했다는 것을 알 수 있습니다.
[2번 코드]
from copy import copy
origin_data = [1, 2, 3, 4, 5]
copy_data1 = origin_data[:]
copy_data2 = copy(origin_data)
# 아래는 위의 2가지 방식과 동일하게 복사되는 방법
# copy_data = list(origin_data)
# copy_data = [] + origin_data
print(f'origin_data: {origin_data} / id: {id(origin_data)}')
print(f'copy_data1: {copy_data1} / id: {id(copy_data1)}')
print(f'copy_data2: {copy_data2} / id: {id(copy_data2)}')
id값이 각각 다르게 나온 것을 보면 [:]
와 copy()
모두 원하던 데이터 복사가 수행된 것을 볼 수 있습니다.
copy_data1, 2
의 데이터를 변경해봤습니다.
copy_data[0] = 10
print(f'origin_data: {origin_data} / id: {id(origin_data)}')
print(f'copy_data: {copy_data} / id: {id(copy_data)}')
copy_data1, 2
를 변경해도 origin_data
가 변경되지 않는 것을 확인할 수 있습니다.
이게 완전히 복사가 된 것일까?
아닙니다, origin_data
의 데이터가 mutable 객체인 경우는 다릅니다.
[3번 코드]
from copy import copy
origin_data = [[1, 2], [3, 4]]
copy_data1 = origin_data[:]
copy_data2 = copy(origin_data)
copy_data1[0][0] = 10
copy_data2[1] = 20
print(f'origin_data: {origin_data} / id: {id(origin_data)}')
print(f'copy_data1: {copy_data1} / id: {id(copy_data1)}')
print(f'copy_data2: {copy_data2} / id: {id(copy_data2)}')
copy_data1
의 0번 인덱스의 데이터는 [1, 2]
로 mutable 데이터입니다.
mutable 데이터 안에 있는 데이터를 변경하는 경우는 origin_data
의 메모리 주소를 복사해서 갖고 있기 때문에 copy_data1, 2
의 데이터가 모두 변하는 것을 확인할 수 있습니다.
3개 변수 모두 : [1, 2] → [10, 2]
하지만 mutable 데이터 전체를 다른 데이터 20
으로 변경한 경우는 정상적으로 copy_data2
만 변경된 것을 확인할 수 있습니다.
copy_data2
만 : [3, 4] → 20
이런 문제를 해결하기 위해서는 4번 방식을 사용해야 합니다.
[4번 코드]
from copy import deepcopy
origin_data = [[1, 2], [3, 4]]
copy_data1 = deepcopy(origin_data)
copy_data1[0][0] = 10
print(f'origin_data: {origin_data} / id: {id(origin_data)}')
print(f'copy_data1: {copy_data1} / id: {id(copy_data1)}')
deepcopy()
함수를 사용한 복사만 mutable 데이터까지 완전하게 복사되는 것을 확인할 수 있습니다.
하지만 그렇다고 deepcopy()
가 만능은 아닙니다.
이유는 복사하는 속도의 차이가 굉장히 크기 때문입니다.
속도가 가장 빠른 슬라이스 형식의 복사 [:]
에 비해서 deepcopy
복사는 30배 이상 느립니다.
그렇기 때문에 상황에 맞는 방식을 적절히 사용해야 합니다.
📌 Stack Overflow - 복사 속도에 대한 내용