Computer >> 컴퓨터 >  >> 프로그래밍 >> Python

Python - 2차원 배열/리스트를 올바르게 사용하는 방법

Python은 2차원 리스트(배열)를 생성하는 다양한 방법을 제공합니다. 하지만 각 방법 사이의 차이점을 정확히 이해하지 못하면, 나중에 원인을 추적하기 매우 어려운 심각한 버그로 이어질 수 있습니다.

잘못된 방식과 올바른 방식의 비교

아래 예제는 두 가지 방식으로 5x5 크기의 2차원 리스트를 만든 뒤, 각각 첫 번째 행의 첫 번째 요소만 1로 변경하고 결과를 출력합니다.

rows, cols = (5, 5)

# 방법 1: 리스트 곱셈으로 생성 (주의 필요)
arr = [[0]*cols]*rows
# 첫 번째 행의 첫 번째 요소를 1로 변경 후 출력
arr[0][0] = 1
for row in arr:
    print(row)

# 방법 2: 리스트 컴프리헨션으로 생성 (권장)
arr = [[0 for i in range(cols)] for j in range(rows)]
# 다시 첫 번째 행의 첫 번째 요소를 1로 변경 후 출력
arr[0][0] = 1
for row in arr:
    print(row)

출력 결과

[1, 0, 0, 0, 0]
[1, 0, 0, 0, 0]
[1, 0, 0, 0, 0]
[1, 0, 0, 0, 0]
[1, 0, 0, 0, 0]
[1, 0, 0, 0, 0]
[0, 0, 0, 0, 0]
[0, 0, 0, 0, 0]
[0, 0, 0, 0, 0]
[0, 0, 0, 0, 0]

왜 이런 결과가 나올까요?

[[0]*cols]*rows 방식에서 리스트에 정수를 곱하면 내부 리스트가 복사되는 것이 아니라 동일한 객체에 대한 참조만 반복됩니다. 즉, 겉보기에는 5개의 행이 있지만 실제로는 모든 행이 하나의 같은 리스트를 가리키고 있는 것입니다.

따라서 arr[0][0] = 1로 한 요소를 변경하면, 그 변경이 공유된 리스트에 그대로 적용되어 모든 행에서 동시에 바뀌어 보입니다. 위 출력 결과 앞부분에서 모든 행의 첫 번째 값이 1로 표시되는 이유가 바로 이것입니다.

반면 리스트 컴프리헨션[[0 for i in range(cols)] for j in range(rows)]은 반복할 때마다 새로운 내부 리스트를 독립적으로 생성합니다. 그래서 특정 행의 요소를 변경해도 다른 행에는 전혀 영향을 주지 않으며, 의도한 대로 첫 번째 행의 값만 변경됩니다.

추가 팁

내부 리스트를 만들 때 [0]*cols처럼 곱셈을 사용하는 것 자체는 문제가 없습니다. 정수 0은 불변(immutable) 객체이므로 한 행 안에서 곱셈으로 채워도 안전합니다. 핵심은 외부 리스트를 만들 때 각 행이 서로 다른 객체가 되도록 반드시 컴프리헨션으로 감싸야 한다는 점입니다.

# 이 방식 역시 안전합니다.
arr = [[0]*cols for _ in range(rows)]

# 두 행이 서로 다른 객체인지 확인
print(arr[0] is arr[1])  # False

정리

2차원 리스트를 생성할 때는 [[0]*cols]*rows와 같은 단순 곱셈 방식을 피하고, 리스트 컴프리헨션을 사용해 각 행이 독립적인 리스트가 되도록 작성하는 것이 안전합니다. 이러한 참조 공유 문제는 프로그램 곳곳에서 원인을 찾기 어려운 버그로 이어질 수 있으므로, 처음부터 올바른 패턴을 습관화하는 것이 좋습니다.