-
빠른 리스트 객체 복사Python 2021. 11. 20. 10:12반응형
한 줄 요약: 내부 데이터가 imutable 객체로 구성된 list type 객체의 깊은 복사가 필요할 때는 slicing을 사용하자.
[mutable type과 깊은 복사의 필요성]
list는 mutable type이다. 따라서 list 변수는 데이터가 존재하는 메모리 주소를 가지고 있는 셈이다.
다음 코드를 보자.
L1 = [1, 2, 3] L2 = l1 print(L1) print(L2)
L1 = [1, 2, 3]이고 L2에 L1을 넣었으니 L1과 L2을 각각 출력하면 동일하게 [1, 2, 3]이다.
이제 L2 [1]의 값을 10으로 변경 후 다시 L1과 L2를 출력해보자.
L2[1] = 10 print(L1) print(L2)
L1과 L2가 모두 [1, 10, 3]으로 변경되는 것을 볼 수 있다. 이 것이 바로 동일한 메모리 참조하는 mutable type의 특징인데, L1과 L2가 동일한 메모리 주소를 가르키기 때문에 어떤 변수를 사용해서 접근하더라도 같은 데이터를 만지게 된다. 따라서 L1과 L2가 완전히 별개의 객체로 다뤄지게 하려면 깊은 복사(deep copy)를 해줄 필요가 있다.
[깊은 복사를 하는 방법]
python은 mutable type 객체의 deep copy를 지원하는 copy 라이브러리를 제공한다.
위의 코드에서 copy 라이브러리를 사용하여 L1과 L2과 별개의 객체가 되도록 변경해보자.
from copy import deepcopy L1 = [1, 2, 3] L2 = deepcopy(L1) print(L1) print(L2) print('----------') L2[1] = 10 print(L1) print(L2)
다음은 위의 코드를 실행한 결과다. L1과 L2과 별개의 객체로 인식되고 있음을 알 수 있다.
python에서 제공하는 slicing 기능을 사용하면 list type의 객체에 대해 보다 간편하고 빠르게 깊은 복사할 수 있다.
L1 = [1, 2, 3] L2 = L1[:] print(L1) print(L2) print('----------') L2[1] = 10 print(L1) print(L2)
실행하면 copy 라이브러리를 사용한 것과 동일한 결과를 보여준다.
주의!
리스트를 구성하는 데이터들이 mutable 객체인 경우는 슬라이싱을 통한 복사를 사용할 수 없다. 엄밀히 말하면 슬라이싱을 통한 리스트 복사는 얕은복사이다. 다음을 예를 살펴보자.
# 2차원 리스트 test1 = [ [1, 2, 3], [4, 5, 6], [7, 8, 9] ] # 슬라이싱으로 복사 test2 = test1[:] # 데이터를 변경하기전 test1, test2를 출력 print(test1) print(test2) # test1[0][0]의 데이터를 변경 test1[0][0] = -1 # test1[0][0]과 test2[0][0]이 모두 변경 되어버림. print(test1) print(test2) # test1[0]과 test2[0]의 객체 id를 확인해보면 # 동일한 객체임을 알 수 있다. print(id(test1[0]), id(test2[0])) print(test1[0] is test2[0]) # 다차원 리스트의 깊은 복사는 copy 라이브러리를 사용하자.
mutable한 데이터를 포함한 리스트의 복사는 copy 라이브러리를 사용한다.
[두 가지 imutable 객체로 구성된 리스트 객체에 대한 두 가지 복사 방법에 대한 속도 비교]
slicing을 사용한 복사는 라이브러리를 사용한 것보다 빠르게 동작한다.
3,000 * 3,000 크기의 2차원 리스트에 대해 깊은 복사를 수행할 때 라이브러리를 사용한 것과 slicing을 사용한 것에 어느 정도 속도 차이가 있는지 확인해보자.
import time from copy import deepcopy N = 3000 M = 3000 test = [[0] * M for _ in range(N)] val = 1 for i in range(N): for j in range(M): test[i][j] = val val += 1 s_time = time.process_time() # deepcopy with library temp1 = deepcopy(test) e_time = time.process_time() print(f"time elapsed : {int(round((e_time - s_time) * 1000))}ms") s_time = time.process_time() # deepcopy with slicing temp2 = [t[:] for t in test] e_time = time.process_time() print(f"time elapsed : {int(round((e_time - s_time) * 1000))}ms")
실행 system에 따라 전반적인 실행 속도의 차이가 있을 수는 있겠으나. 라이브러리와 slicing을 사용한 깊은 복사 속도는 엄청나게 큰 차이가 있음을 알 수 있다.
'Python' 카테고리의 다른 글
Python 실행속도 개선 방법 (4) 2021.10.04 itertools - product(), permutations(), combinations() (0) 2021.09.01 all(), any() 함수 (0) 2021.08.31 여러 iterable 객체를 묶어주는 zip 내장함수 (0) 2021.07.02 for ~ else 문 (0) 2021.07.02