👉 [Python] 같지만 다른 배열 초기화 방법
x = [[0] * n ] * n
종종 이런식으로 이차원 배열을 선언하는데 알 수 없는 오류가 발생했다.
arr2 = [[0] * n ] * n
arr2[0][0] = 1
print(arr2)
# 출력: [[1, 0, 0], [1, 0, 0], [1, 0, 0]]
분명 첫 원소 (0,0)만 바꿨는데 (X,0) 애들이 전부 바뀌어 버린다... 😭
다른 방식과 비교해보았다.
n = 3
# 리스트 내포로 2차원 리스트 초기화
arr1 = [[0] * n for _ in range(n)]
arr1[0][0] = 1
print(arr1)
# 출력: [[1, 0, 0], [0, 0, 0], [0, 0, 0]]
# 반복문으로 2차원 리스트 초기화
arr2 = [[0] * n ] * n
arr2[0][0] = 1
print(arr2)
# 출력: [[1, 0, 0], [1, 0, 0], [1, 0, 0]]
x = [[0] * n for _ in range(n)]
y = [[0] * n ] * n
두 배열의 출력 결과값은 N*N 행렬의 2차원 배열로 같다.
a = [[0] * 5 ] * 5
print(id(a[0]))
print(id(a[1]))
print()
b = [[0] * 5 for _ in range(5)]
print(id(b[0]))
print(id(b[1]))
id() 리턴 값인 같다. 같은 객체인 것이다.
answering my own question, because its a bit hard to find a good explanation:
in python, lists are referential structures so [0] * n creates a list of pointers, pointing to an object with value int(0). This is fine for 1d arrays, because arr[5] = 10 makes the 5th index in the array now point to a new python object with value int(10).
This becomes an issue with 2D arrays created as [[0] * n] * m], makes each each arr[m] point to a the same list. So modifying any arr[I][j] would modify all arr[:][j].This can be a big unseen bug while trying to solve problems in interview, so keep an eye out and always use for list comprehension for n-d arrays for d > 1.
해외 댓글인데, Python lists가 포인팅의 역할을 하고 있기 때문에 같은 일차원 리스트를 포인팅하고 있기 때문이란다.
첫 번째 예제에서는 리스트 내포(List comprehension)를 사용하여 2차원 리스트를 초기화한다. `arr1 = [[0] * n for _ in range(n)]` 코드는 내부적으로 `n`개의 0으로 채워진 리스트를 생성하고, 그 리스트를 `n`번 반복하여 2차원 리스트를 만든다. 이 방법은 각각의 리스트가 서로 다른 객체이기 때문에 각 원소를 독립적으로 변경할 수 있다.
두 번째 예제에서는 반복문을 사용하여 2차원 리스트를 초기화한다. `arr2 = [[0] * n ] * n` 코드는 내부적으로 `n`개의 0으로 채워진 리스트를 생성하고, 그 리스트를 `n`번 반복하여 2차원 리스트를 만든다. 이 방법은 각각의 리스트가 동일한 객체를 참조하기 때문에 한 리스트를 변경하면 다른 리스트도 변경된다.
따라서, `arr2[0][0] = 1` 코드를 실행하면 `arr2`의 모든 리스트의 첫 번째 원소가 1로 변경되어 출력 결과가 `[[1, 0, 0], [1, 0, 0], [1, 0, 0]]`와 같이 나타나게 된다.
따라서, 리스트 내포를 사용하여 2차원 리스트를 초기화하는 것이 일반적으로 안전하다. 이거 때문에 날려 먹는 시간이 꽤 많았다.