파이썬/기초 프로그래밍

파이썬 리스트 곱하기(*)와 참조 공유 문제

Data Jun 2025. 9. 7. 13:52

파이썬에서 2차원 리스트를 만들 때는 종종 다음과 같이 두 가지 방식이 사용됩니다.

# 방법 1: 리스트 내포 (list comprehension)
marks1 = [['~'] * 3 for _ in range(4)]

# 방법 2: 곱하기 연산자 (*)
marks2 = [['~'] * 3] * 4

겉보기에는 똑같이 4x3 형태의 2차원 리스트처럼 보이지만, 내부 동작은 전혀 다릅니다.

 

1. 리스트 내포 방식 (marks1)

marks1 = [['~'] * 3 for _ in range(4)]
  • 리스트 내포를 사용할 경우, for 반복마다 새로운 리스트 객체가 생성됩니다.
  • 따라서 marks1의 각 행은 서로 독립된 객체입니다.
marks1[0][1] = 'X'
print(marks1)
# [['~', 'X', '~'], ['~', '~', '~'], ['~', '~', '~'], ['~', '~', '~']]

첫 번째 행만 변경되고, 나머지 행에는 영향이 없습니다.

 

2. 곱하기 연산자 방식 (marks2)

marks2 = [['~'] * 3] * 4​
  • ['~'] * 3 리스트 하나를 만든 뒤, 그 동일한 객체를 4번 참조하는 구조입니다.
  • 따라서 4개의 행은 사실 모두 같은 리스트를 공유합니다.
marks2[0][1] = 'X'
print(marks2)
# [['~', 'X', '~'], ['~', 'X', '~'], ['~', 'X', '~'], ['~', 'X', '~']]

한 행을 수정했는데, 모든 행이 동시에 바뀌어버렸습니다.

 

3.참조 구조 확인하기

print([id(i) for i in marks1])
print([id(i) for i in marks2])

 

이 현상의 이름은?

많은 분들이 이를 깊은 복사 vs 얕은 복사 문제라고 표현하지만, 사실 더 정확한 표현은 참조 공유(reference sharing) 문제입니다.

  • marks1 → “깊은 복사처럼” 각 행이 독립적
  • marks2 → “얕은 복사보다 더 심각한” 동일 객체 참조

즉, 리스트 내포는 안전하게 독립 객체를 만들고, * 연산자는 같은 객체를 반복 참조한다고 이해하는 것이 핵심입니다.

 

 

 

정리하면

 

파이썬에서 2차원 리스트를 만들 때는 [['~'] * 3 for _ in range(4)]처럼 리스트 내포를 쓰는 것이 안전합니다.
곱하기(*) 연산자를 쓰면 의도치 않게 모든 행이 연결되어 버릴 수 있으니 주의해야 합니다.