2차원 리스트 사용하기

Tasker_Jang·2026년 2월 18일
post-thumbnail

1. 2차원 리스트란?

2차원 리스트는 가로(열) × 세로(행) 형태로 이루어진 리스트 안의 리스트입니다. 행(row)과 열(column) 모두 0부터 시작합니다.

# 3행 4열의 2차원 리스트
matrix = [
    [1, 2, 3, 4],     # 행 0
    [5, 6, 7, 8],     # 행 1
    [9, 10, 11, 12]   # 행 2
]

시각적으로 표현하면 다음과 같습니다.

열 0열 1열 2열 3
행 01234
행 15678
행 29101112

2. 요소 접근과 값 할당

2차원 리스트의 요소에 접근하거나 값을 할당할 때는 대괄호 []를 두 번 사용합니다. 첫 번째가 행, 두 번째가 열입니다.

# 요소 접근: 리스트[행][열]
print(matrix[1][2])  # 7 (행 1, 열 2)

# 값 할당
matrix[0][3] = 99
print(matrix[0])  # [1, 2, 3, 99]

3. 톱니형 리스트 (Jagged List)

파이썬에서는 각 행의 길이가 다른 톱니형 리스트도 만들 수 있습니다. 다른 언어에서는 제약이 있는 경우가 많지만, 파이썬은 리스트 안에 서로 다른 길이의 리스트를 자유롭게 넣을 수 있습니다.

# 각 행의 길이가 다른 톱니형 리스트
jagged = [
    [1, 2],
    [3, 4, 5, 6],
    [7, 8, 9]
]
print(jagged[1][3])  # 6

4. 2차원 튜플

2차원 튜플도 존재합니다. 다만 튜플은 내용을 변경할 수 없으므로, 조회만 가능하다는 점에 유의해야 합니다.

# 2차원 튜플
matrix_tuple = (
    (1, 2, 3),
    (4, 5, 6),
    (7, 8, 9)
)
print(matrix_tuple[2][1])  # 8

# 값 변경 시도 → 에러 발생
# matrix_tuple[0][0] = 99  # TypeError!

5. 반복문으로 2차원 리스트 순회하기

방법 1: for-in으로 직접 순회

가장 간결한 방법입니다. 바깥 for에서 행을 꺼내고, 안쪽 for에서 각 요소를 꺼냅니다.

# for-in 중첩으로 모든 요소 출력
matrix = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]

for row in matrix:
    for element in row:
        print(element, end=' ')
    print()  # 행이 끝나면 줄바꿈
# 1 2 3
# 4 5 6
# 7 8 9

방법 2: for-range로 인덱스 접근

인덱스가 필요한 경우 range를 활용합니다.

# for-range로 인덱스를 사용한 접근
for i in range(len(matrix)):
    for j in range(len(matrix[i])):
        print(f"[{i}][{j}] = {matrix[i][j]}", end='  ')
    print()

방법 3: while 반복문

while로도 동일하게 순회할 수 있습니다. 인덱스를 직접 관리해야 하므로 코드가 다소 길어집니다.

# while 반복문으로 2차원 리스트 순회
i = 0
while i < len(matrix):
    j = 0
    while j < len(matrix[i]):
        print(matrix[i][j], end=' ')
        j += 1
    print()
    i += 1

💡 일반적으로는 for-in 방식이 가장 파이썬답고 간결합니다. 인덱스가 꼭 필요한 경우에만 rangeenumerate를 사용하세요.


6. 리스트 컴프리헨션으로 2차원 리스트 만들기

기본 2차원 리스트 생성

리스트 컴프리헨션을 중첩하면 2차원 리스트를 간결하게 만들 수 있습니다.

# 3행 4열 2차원 리스트 생성 (모든 요소 0으로 초기화)
matrix = [[0 for col in range(4)] for row in range(3)]
print(matrix)
# [[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]]

톱니형 리스트도 컴프리헨션으로

# 톱니형 리스트: 각 행의 길이가 행 번호 + 1
jagged = [[0 for col in range(row + 1)] for row in range(3)]
print(jagged)
# [[0], [0, 0], [0, 0, 0]]

⚠️ 주의: * 연산자로 만들면 안 되는 이유

2차원 리스트를 만들 때 흔히 하는 실수가 있습니다.

# ❌ 잘못된 방법: 같은 리스트를 참조하게 됨
wrong = [[0] * 4] * 3
wrong[0][0] = 99
print(wrong)
# [[99, 0, 0, 0], [99, 0, 0, 0], [99, 0, 0, 0]]  ← 모든 행이 변경됨!

# ✅ 올바른 방법: 리스트 컴프리헨션 사용
correct = [[0] * 4 for _ in range(3)]
correct[0][0] = 99
print(correct)
# [[99, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]]  ← 행 0만 변경됨

* 연산자로 행을 복제하면 모든 행이 같은 리스트 객체를 참조하게 되어, 하나를 바꾸면 전부 바뀝니다. 이전 포스트에서 다룬 할당 vs 복사 문제와 동일한 원리입니다.


7. deepcopy — 깊은 복사

이전 포스트에서 copy() 메서드를 다뤘는데, 이는 얕은 복사(shallow copy)였습니다. 2차원 리스트처럼 중첩된 구조에서는 copy()로는 부족합니다.

deepcopy는 중첩된 리스트(튜플) 안에 있는 모든 리스트(튜플)까지 재귀적으로 복사해줍니다.

import copy

# 얕은 복사: 내부 리스트는 여전히 공유
a = [[1, 2], [3, 4]]
b = a.copy()
b[0][0] = 999
print(a)  # [[999, 2], [3, 4]] ← a도 변경됨!

# 깊은 복사: 완전히 독립된 리스트
a = [[1, 2], [3, 4]]
c = copy.deepcopy(a)
c[0][0] = 999
print(a)  # [[1, 2], [3, 4]] ← a는 변경되지 않음

얕은 복사와 깊은 복사의 차이를 정리하면 다음과 같습니다.

구분copy() / list()copy.deepcopy()
복사 깊이최상위 리스트만 복사모든 중첩 리스트까지 복사
내부 리스트원본과 공유완전히 독립
사용 시점1차원 리스트2차원 이상 중첩 리스트

profile
ML Engineer 🧠 | AI 모델 개발과 최적화 경험을 기록하며 성장하는 개발자 🚀 The light that burns twice as bright burns half as long ✨

0개의 댓글