Python

[python] 2차원 이상의 리스트(배열) 복사하기

inmonim 개발개발 2025. 1. 19. 16:53

visited 배열을 만들 때 하는 실수들

N = 10

visited = [[0] * N] * 10

 

Python으로 알고리즘 문제 풀이를 할 때, 위와 같이 코드를 짜면 문제가 발생합니다!

 

visited[0][1] = 1

print(visited[5][1])

>>> 1       / ?????

 

이는 리스트 내부의 0번 부터 9번까지의 리스트가 똑같은 메모리를 참조하기 때문에 벌어지는 문제입니다.

 

즉, 0번째 리스트의 1번째 값을 1로 바꿨으나, 실제로는 모든 2차원 배열의 1열 전체가 1로 바뀌는 것이죠.

 

[0] * N 으로 생성된 리스트 객체가 c8이라는 메모리를 가졌다고 하면,

 

[[0] * N] * M으로 생성한 visited 리스트 내부는 [c8, c8, c8, c8, c8...] 이렇게 되어있는 것입니다!

 

이러한 문제는 어째서 발생하고, 2차원 이상 리스트를 복사하려면 어떻게 해야 할까요?

 

2차원 이상의 리스트 복사하기

내부의 요소가 모드 기본 자료형인 1차원 배열과 달리,

 

2차원 배열부터는 리스트를 복사할 때 의도치 않은 문제가 발생하곤 합니다.

 

보통 알고리즘 문제를 풀 때 그러한데,

 

BFS 문제로 visited를 만들 때기존 리스트를 복사할 때 문제가 발생합니다.

 

먼저 기존의 리스트와 똑같은 요소를 갖고 있지만,

 

변경 사항이 전혀 공유되지 않는 독립된 리스트 만들기를 알아봅시다!

 

깊은 복사 - 값은 똑같지만 완전히 독립된 객체 생성

이는 내부의 모든 요소를 새로운 메모리에 할당하여 새 객체를 만드는 것을 의미합니다.

 

copy 모듈의 deepcopy 함수를 쓰면 됩니다.

 

당연히 내부의 모든 값을 새로운 객체로 만들기 때문에 그 즉시 메모리가 * 2 됩니다!

from copy import deepcopy

origin_list = [[1, 2], [2, 4], [4, 6]]

copy_list = deepcopy(origin_list)

print(origin_list[0][0] is copy_list[0][0])

# False

 

그러나 deepcopy를 사용할 경우, 내부의 모든 요소들이 새로운 메모리에 할당되어 새 객체가 됩니다.

 

따라서, 리스트 객체 자체는 물론 모든 요소들이 원본 리스트 및 그 내부 요소와 완전 독립되어 있습니다.

 

한 개의 리스트에서 값을 바꾸고 원본과 비교하는 방법을 쓸 것이라면, 이를 활용할 수 있습니다.

 

이를 제외한 다른 거의 모든 방법에서 가장 바깥 리스트를 제외한 내부의 요소들의 메모리 주소는 똑같이 설정됩니다.

 

즉, 껍데기만 갈아치울 뿐이죠.

 

바로 이 문제 때문에 2차원 이상의 배열에서는 바뀐 값이 공유되는 문제가 벌어집니다.

deepcopy 이외의 대부분의 경우...(얕은 복사)

list생성자 - list(arr)

 

얕은 복사 메서드 - list.copy()

 

슬라이싱 객체 대입 - list[:]

 

컴프리헨서(재귀로 모든 내부 요소를 갈아치우면 deepcopy입니다.) - [i for i in ~~]

A = [[1, 2], ['X', 'Y']]

B = A.copy()

A[1][0] = 'Z'

print(B[1][0])

# Z

 

위와 같이 원본 리스트에서 2depth 이상 내려간 요소를 변경할 경우,

 

해당 변경사항이 원본 객체 뿐만 아니라 복사한 객체에도 적용됩니다.

A = [[1, 2], ['X', 'Y']]

B = A.copy()

print(A is B)

# False

print(A[0] is B[0])

# True

print(A[0][1] is B[0][1])

# True

 

위의 경우처럼 말이죠!

 

즉, A나 B나 내부 리스트와 요소들이 같은 메모리를 참조한다는 것인데

 

아예 그냥 동일한 객체가 다른 리스트에 담겨있을 뿐이죠.

 

A와 B가 똑같이 생긴 물건을 가지고 있는 게 아니라,

 

대여점에 하나 뿐인 물건을 번갈아 가져오는 것입니다.

 

A가 책에 코딱지를 붙여 놓으면, 당연히 이후에 빌린 B가 본 책에는 코딱지가 붙어있습니다.

 

그렇기 때문에 2차원 이상의 배열을 원본과 완전히 독립된 객체로 만들기 위해서는

 

내부를 완전히 풀어헤치는 재귀 컴프리헨서를 쓰거나 해야합니다!

 

그러니 깊은 복사가 필요한 경우, 편하게 deepcopy를 씁시다!

 

다만, 2차원 배열을 만들어야 할 경우에는 [list for _ in range(n)]와 같이 컴프리헨션을 쓰는 것을 조금 더 추천드립니다!