Python - 2차원 리스트 생성 방식과 차이점 정리

2 분 소요

파이썬 2차원 리스트 생성 방식과 차이점 정리

1. 문제 배경

파이썬에서 2차원 리스트(배열)를 만들 때, 겉보기에는 동일해 보이는 코드라도 내부 메모리 구조가 완전히 달라질 수 있습니다.

이 차이를 이해하지 못하면 다음과 같은 문제가 발생합니다.

  • BFS/DFS에서 visited 배열이 비정상적으로 동작
  • 한 칸만 수정했는데 여러 행이 동시에 바뀜
  • 원인을 찾기 어려운 논리 오류 발생

2. 실험 코드

아래는 2차원 리스트를 만드는 대표적인 3가지 방법입니다.

N = 3
M = 4

# 방법 A: for문 + append
a = []
for _ in range(N):
    row = [False] * M
    a.append(row)

# 방법 B: 리스트 컴프리헨션
b = [[False] * M for _ in range(N)]

# 방법 C: 곱셈(*) 사용
c = [[False] * M] * N

print("초기 a:", a)
print("초기 b:", b)
print("초기 c:", c)

print("\n--- 한 칸 수정: [0][0] = True ---")
a[0][0] = True
b[0][0] = True
c[0][0] = True

print("수정 후 a:", a)
print("수정 후 b:", b)
print("수정 후 c:", c)

3. 초기 출력 결과 (왜 다 똑같아 보이는가?)

초기 a: [[False, False, False, False], [False, False, False, False], [False, False, False, False]]
초기 b: [[False, False, False, False], [False, False, False, False], [False, False, False, False]]
초기 c: [[False, False, False, False], [False, False, False, False], [False, False, False, False]]

설명

  • 세 방식 모두 초기 값(value) 만 보면 동일
  • 이 시점에서는 차이가 드러나지 않는 것이 정상
  • 문제는 수정(mutation) 이 발생할 때 나타남

4. 수정 후 결과 비교 (핵심 차이 발생)

수정 후 a: [[True, False, False, False], [False, False, False, False], [False, False, False, False]]
수정 후 b: [[True, False, False, False], [False, False, False, False], [False, False, False, False]]
수정 후 c: [[True, False, False, False], [True, False, False, False], [True, False, False, False]]

5. 각 방식의 내부 동작 원리

5.1 방법 A – for문 + append (정상)

a = []
for _ in range(N):
    row = [False] * M   # 매번 새로운 리스트 생성
    a.append(row)

구조 개념

  • row서로 다른 리스트 객체
  • 행 간 메모리 공유 없음

  • 안전
  • 직관적
  • BFS/DFS 사용 가능

5.2 방법 B – 리스트 컴프리헨션 (정상, 권장)

b = [[False] * M for _ in range(N)]

내부적으로는 다음과 동일

b = []
for _ in range(N):
    b.append([False] * M)

구조 개념

  • 반복마다 새 리스트 생성
  • 모든 행이 독립적인 객체

가장 많이 쓰는 방식

  • 가독성 좋음
  • 코딩 테스트 표준 방식

5.3 방법 C – 곱셈(* N) 방식 (문제 발생)

c = [[False] * M] * N

실제 동작 방식

row = [False] * M
c = [row, row, row]   # 같은 리스트를 N번 참조

구조 개념

  • 모든 행이 같은 리스트 객체를 공유
  • 한 칸 수정 → 모든 행에 동시에 반영

BFS/DFS에서 사용 금지 visited 배열로 절대 사용하면 안 됨


6. 차이를 표로 정리

구분 방법 A (append) 방법 B (컴프리헨션) 방법 C (* N)
초기 출력 동일 동일 동일
행 객체 서로 다름 서로 다름 모두 동일
메모리 공유 없음 없음 있음
한 칸 수정 영향 해당 행만 해당 행만 모든 행
BFS/DFS 사용 가능 가능 (권장) ❌ 불가

7. 왜 이런 문제가 중요한가?

BFS/DFS에서 흔히 사용하는 코드

visited[nr][nc] = True
  • visited 행이 공유된 상태라면

    • 의도하지 않은 위치까지 방문 처리
    • 탐색 경로 왜곡
    • 정답 불일치 발생

겉보기엔 정상인데, 결과만 틀리는 가장 위험한 버그 유형


8. 실무·코딩테스트 암기 문장

2차원 리스트는 반드시 컴프리헨션으로 생성한다. [[x]*M]*N 은 값은 같아 보여도 구조가 다르다.


9. 권장 패턴 정리

# 2차원 boolean 배열
visited = [[False] * M for _ in range(N)]

# 2차원 거리 배열
dist = [[-1] * M for _ in range(N)]

10. 핵심 요약

  • 출력 결과만 보고 구조를 판단하면 안 된다
  • 차이는 “수정 시점” 에 나타난다
  • * N 은 값 복사가 아니라 참조 복사
  • BFS/DFS, DP, 그래프 문제에서는 컴프리헨션이 표준

태그:

카테고리:

업데이트:

댓글남기기