Programming/Python

[Python] 파이썬 리스트 복사 : 시간 초과 해결, 깊은 복사와 얕은 복사, copy, deepcopy, slicing, 2차원 리스트 복사

728x90

 

백준 풀이를 하다 리스트를 깊은 복사할 때, deepcopy()를 사용한 코드가 slicing을 사용한 코드보다 7배 이상의 시간이 걸린 것을 확인하고 정리해봐야겠다는 생각이 들었습니다!

가뜩이나 느린 파이썬에서 시간이 이렇게 감소한다면 충분히 공부해 볼 가치가 있죠 😤

 

파이썬 시간을 단축시키는 방법으로는 입출력 시간을 단축시키는 것도 있습니다! 다음 글에 정리해두었습니다

📌  https://codesyun.tistory.com/178

 

[Python] 파이썬 sys.stdin.readline() 입력 받기 : 시간 초과 해결, 입출력 속도 개선

🤔 input() 대신 sys.stdin.readline() 을 사용하는 이유 한두줄 입력받는 문제들은 input()을 사용해도 괜찮을 수 있지만, 여러줄 또는 반복문으로 입력 받는 경우에는 input()은 시간초과가 발생할 수 있

codesyun.tistory.com

 

 

깊은 복사 / 얕은 복사에 대해서도 다시 짚고 넘어가며 리스트 복사 방법에 대해 알아보겠습니다 🔥

 

 

 

 

 

복사 (Copy)

알고리즘 풀이를 하다보면 원본 리스트를 보존하기 위해 리스트를 복사해야 할 필요를 느끼곤 합니다

복사에 대해 제대로 알지 못한 채로 함부로 복사하면, 원본 객체가 변경되어 큰 문제가 생길 수 있습니다

파이썬에서 복사는 '='연산자와 copy 모듈의 copy(), deepcopy()slicing을 통해 수행할 수 있습니다

 

 

 

> 얕은 복사 (Shallow Copy)

: 원본 객체의 주소 값을 복사하는 것

 

> 깊은 복사 (Deep Copy)

: 참조 주소 값이 아닌 참조된 객체 자체를 복사하는 것

 

 

 

> '=' 연산자

import copy

origin_list = [1, 2, 3]

copy_list_a = origin_list               # '=' operator

 

origin_list = [1, 2, 3]
copy_list = origin_list

print(id(origin_list))  # 1713539575424
print(id(copy_list))    # 1713539575424

print(origin_list)      # [1, 2, 3]
print(copy_list)        # [1, 2, 3]

copy_list[0] = 5
print(origin_list)      # [5, 2, 3]
print(copy_list)        # [5, 2, 3]

- 원본 리스트와 복사된 리스트가 같은 주소를 가지고 같은 객체를 가리키고 있다

- 따라서 복사된 리스트를 변경하면 원본 리스트 또한 변경된다

 

- 불변형(immutable) 객체에서는 복사한 객체에 데이터를 변경해도 원본 객체에 영향이 없지만, 리스트와 같은 변형(mutable) 객체에서는 복사한 객체의 변경이 원본 객체에도 영향을 미친다

 

 

 

 

 

> copy 모듈의 copy() 함수   /    List Slicing [:] 

import copy

origin_list = [1, 2, 3]

copy_list_a = copy.copy(origin_list)    # copy() function of copy module
copy_list_b = origin_list[:]            # list slicing

 

import copy

origin_list = [1, 2, 3]
copy_list = copy.copy(origin_list)

print(id(origin_list))  # 4378158400
print(id(copy_list))    # 4378158272

print(origin_list)      # [1, 2, 3]
print(copy_list)        # [1, 2, 3]

copy_list[0] = 5
print(origin_list)      # [5, 2, 3]
print(copy_list)        # [5, 2, 3]

- 원본 리스트와 복사된 리스트는 각기 다른 객체이다

- 따라서 복사된 리스트를 변경하여도 원본 리스트에는 변화가 없다

- 하지만 다음과 같이 이차원 배열을 복사하면, 각 요소인 배열을 변경할 경우 원본에 영향을 미친다

origin_list = [[1, 1], [2, 2], [3, 3]]
copy_list = origin_list[:]

print(origin_list)      # [[1, 1], [2, 2], [3, 3]]
print(copy_list)        # [[1, 1], [2, 2], [3, 3]]

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

 

 

 

 

 

> copy 모듈의 deepcopy() 함수

- 다차원 배열을 완전히 복사하기 위해선 deepcopy 함수를 사용해야 한다

    . 리스트 슬라이싱을 응용하여 다차원배열을 완전 복사하는 것 또한 가능하다

import copy

origin_list = [1, 2, 3]

copy_list_a = copy.deepcopy(origin_list)    # deepcopy() function of copy module

 

import copy

origin_list = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]

# deepcopy()
copy_list_a = copy.deepcopy(origin_list)

# list slicing
copy_list_b = [item[:] for item in origin_list]

 

- 다차원 배열 복사의 방법별 처리 속도

  • 10.59 sec (105.9 µs/itn) - copy.deepcopy(old_list)
  • 10.16 sec (101.6 µs/itn) - pure Python Copy() method copying classes with deepcopy
  • 1.488 sec (14.88 µs/itn) - pure Python Copy() method not copying classes (only dicts/lists/tuples)
  • 0.325 sec (3.25 µs/itn) - for item in old_list: new_list.append(item)
  • 0.217 sec (2.17 µs/itn) - [i for i in old_list] (a list comprehension)
  • 0.186 sec (1.86 µs/itn) - copy.copy(old_list)
  • 0.075 sec (0.75 µs/itn) - list(old_list)
  • 0.053 sec (0.53 µs/itn) - new_list = []; new_list.extend(old_list)
  • 0.039 sec (0.39 µs/itn) - old_list[:] (list slicing)

- list slicing이 가장 빠르고 deepcopy가 가장 느린 것을 확인할 수 있습니다!

 

 

 

 

 

 

 

 

[참고] https://stackoverflow.com/questions/2612802/list-changes-unexpectedly-after-assignment-why-is-this-and-how-can-i-prevent-it

 

List changes unexpectedly after assignment. Why is this and how can I prevent it?

While using new_list = my_list, any modifications to new_list changes my_list every time. Why is this, and how can I clone or copy the list to prevent it?

stackoverflow.com

 

 

728x90