파이선에서 n차원 배열을 초기화 할 일은 꽤 많습니다. 어제도 그러한 코드를 작성했거든요. n = 2일 때만 작성해 보도록 하겠습니다. n > 2이상일 때는 2일 때만 잘 응용하면 되기 때문입니다. 어떻게 해야 하는지 예제 코드들을 보도록 하겠습니다.

 


요구 사항은 int형 Object를 저장할 2차원 배열을 만들라는 것입니다. 저는 9x9짜리 배열을 만들었습니다.

 

 

 그런데, 이 코드는 어떻게 동작할까요? 저는 2번째 줄에서 arr[0][0]만 1로 바꾸었습니다.

 

 

 그런데, arr[0][0]과 arr[1][0]을 출력해 보니, 둘 다 1이 나왔습니다. 왜 그럴까요? 어디선가 주소값만 복사되는 얕은 복사가 일어났기 때문입니다.

 

 

 python에서 id는 해당 객체의 값을 얻어오는 코드입니다. 9개의 루프를 돌면서 얻어와 보도록 하겠습니다.

 

 

 그랬더니, 1949457175424라는 같은 값만 계속 출력됨을 확인할 수 있어요. 쉽게 말해서, 같은 객체의 참조 값이 복사된 셈입니다. arr = [[0]*9]*9라고 선언했으니, 0이 9개 있는 같은 배열 객체 9개가 주루룩 선언이 된 셈입니다. 이걸 그려보면 아래 그림과 같습니다.

 

 

 이런 상황인 셈입니다. 여기서 arr[0][0]을 바꾸면 그림에서 array object의 [0]번째 원소를 바꾸는 것이기 때문에, arr[1][0]의 값도 1이 되어 버립니다. 얕은 복사가 일어난 셈입니다. 여기서 int가 불변 객체인지는 중요하지 않습니다. array object가 불변 객체가 아니였다는 점이 중요합니다.

 


아래와 같이 코드를 바꿔보겠습니다.

 

 

 arr에 [0]*9를 append 하는 식으로 코드를 작성하였습니다. 그러면 어떻게 될까요?

 

 

 arr[0]과 arr[1], ... , arr[8]의 객체 id 값이 모두 다릅니다. 이 상황을 그림으로 그려보면 아래와 같습니다.

 

 

 이 상황은 알겠습니다. 그런데, 다음과 같은 경우라면 이야기가 또 달라집니다.

 

 

 이런 경우라면 어떤가요? arr[0][0]과 arr[0][1]이 가리키는 객체가 같다면, 문제가 생길 수 있을 겁니다. 그런데, int Object가 불변 객체라면 어떨까요?

 

 

 디버거를 돌려 봅시다. arr[0][0]과 arr[0][1]의 id 값을 출력해 보았더니, 둘 다 같았습니다.

 

 

 그런데 실제로 list에 있는 값들을 조회해 보니, arr[0][0]과 arr[0][1]의 값이 달랐습니다. 이는 int가 immutable 하다는 이야기입니다. setXXX 어쩌고가 호출이 되면, 새로운 객체를 생성했다는 이야기가 되겠습니다. 저는 append를 이용해서 초기화를 많이 하였습니다.

 


 그렇지만, 이렇게 코딩하는 방법도 있습니다.

 

 

 [[0]*9 for _ in range (9)]. list comprehension이라고 하는 문법인데요. 이게 어떻게 동작하는지, id(arr[i])값을 찍어보면서 보도록 하겠습니다.

 

 

 그러면, arr[0]의 id 값과 arr[1]의 id값이 다름을 확인할 수 있습니다.

 

 

 그리고 arr[0][0]과 arr[1][0], arr[0][0]과 arr[0][1]이 다른 값을 가짐을 볼 수 있습니다. 문제는 2차원 배열에 들어있는 array가 불변 객체인 경우에는 요렇게 코딩해도 되는데, 변할 수 있는 객체면 이야기가 달라진다는 것입니다.

 

 

 6번째 코드를 보면 [Obj(5)]*9 짜리를 9개 생성하고 있습니다. 일단 arr[0]과 arr[1] 이것은 별 문제가 되지는 않습니다. 중요한 것은, 6번째 문장을 수행하고 나서 메모리의 상태는 아래와 같이 되었다는 것인데요.

 

 

 이 상태에서 arr[0][0].set(6)을 호출하면, 노란색 부분의 object가 바뀝니다.

 

 

 그런데, arr[0][1]도 노란색 객체를 보고 있기 때문에 의도치 않게 arr[0][1]의 값도 바뀌어 버립니다.

 

 

 디버그 창을 보면 위와 같습니다. 이런 경우에는 어떻게 초기화를 하면 좋을까요? age가 int Object만 들어온다고 가정합시다.

 

 

 안쪽의 배열을 [Obj(5)]*9로 초기화 하는 대신에, list comprehension을 써서 루프를 돌 때 마다 새로운 객체를 보게끔 하면 됩니다.

 

 

 그렇게 코딩을 하면, arr[0][0]의 값을 바꾸더라도 arr[0][1]의 값에는 영향이 없음을 알 수 있습니다. r행 c열의 2차원 int 배열을 초기화 하려면 어떻게 하면 될까요?

 

 

 요렇게 하시면 됩니다. 안쪽에서 c번 돌면서 c개의 열을 초기화 하고 바깥 쪽에서 r번 돌리면서 r개의 행을 초기화 하면 됩니다.

 

 

 arr을 출력해 보니, 의도대로 잘 나오네요.