반복의 덩어리 안에 반복을 또 넣으면, 바깥 반복이 한 바퀴 돌 때마다 안쪽 반복이 처음부터 끝까지 다 돕니다. 이런 반복을 겹친 반복(중첩 반복, nested loop)이라고 부릅니다.
2단을 찍는 데는 반복 하나면 됩니다. 2단과 3단을 다 찍으려면 단마다 그 반복을 한 번씩 되풀이해야 합니다. 반복을 반복하는 것입니다. 메뉴와 컵 크기의 모든 짝을 늘어놓을 때처럼 두 가지를 짝지어 볼 때도 같은 꼴을 씁니다.
1바깥 한 바퀴마다 안쪽은 처음부터 끝까지
2단과 3단에 1부터 3까지 곱한 값을 찍습니다. 바깥 반복의 덩어리 안에 안쪽 반복이 있고, 안쪽 반복의 덩어리는 8칸 들여 씁니다.
for i in range(2, 4):
for j in range(1, 4):
print(i, j, i * j)
2 1 2
2 2 4
2 3 6
3 1 3
3 2 6
3 3 9찍힌 차례를 봅니다. i가 2인 동안 j가 1, 2, 3을 다 돈 뒤에야 i가 3이 됩니다. 그리고 j는 다시 1부터 셉니다.
- 1번 줄: 바깥 반복이 2를 꺼내 그 값에
i를 붙입니다. - 2~3번 줄: 안쪽 반복이 1, 2, 3을 차례로 꺼내 그 값에
j를 붙이며 3번 줄을 세 번 실행합니다. - 안쪽 반복이 끝나면 바깥 덩어리의 끝이라 1번 줄로 돌아가 3을 꺼내 그 값에
i를 붙입니다. - 2번 줄이 다시 실행되면 안쪽 반복은
range(1, 4)를 처음부터 다시 꺼냅니다.
추적 표로 바깥 첫 바퀴를 따라가면 이렇습니다.
| 줄 | 바뀐 이름 | 값 | 출력 |
|---|---|---|---|
| 1 | i | 2 | |
| 2 | j | 1 | |
| 3 | 2 1 2 | ||
| 2 | j | 2 | |
| 3 | 2 2 4 | ||
| 2 | j | 3 | |
| 3 | 2 3 6 | ||
| 2 | 꺼낼 수 없음 — 안쪽 반복 끝 | ||
| 1 | i | 3 | |
| 2 | j | 1 | |
| … |
안쪽 반복은 바깥 덩어리 안에 있는 줄이라, 바깥 한 바퀴마다 통째로 실행됩니다(규칙 4).
2몇 번 실행되나 — 바깥 바퀴 × 안쪽 바퀴
아래 코드는 무엇을 찍을까요? 답을 정한 뒤 펼쳐 보세요.
count = 0
for i in range(2, 4):
for j in range(1, 4):
count = count + 1
print(count)
실행 결과 보기
6예상과 다르면 그곳이 배울 곳입니다. 바깥이 2바퀴이고, 바깥 한 바퀴마다 안쪽이 3바퀴를 돕니다. 그래서 4번 줄은 2 + 3 = 5번이 아니라 2 × 3 = 6번 실행됩니다. 2단·3단 곱셈표가 2줄 × 3칸 = 6칸인 것과 같습니다.
3들여쓰기로 정하는 자리
겹친 반복 안의 줄은 들여쓰기가 0칸, 4칸, 8칸 가운데 하나입니다. 몇 칸이냐에 따라 그 줄이 실행되는 횟수가 정해집니다.
단마다 곱을 다 찍은 뒤 '2단 끝'을 한 번 찍으려면, 그 줄을 안쪽 for 줄과 같은 4칸에 둡니다.
for i in range(2, 4):
for j in range(1, 4):
print(i * j)
print(f'{i}단 끝')
2
4
6
2단 끝
3
6
9
3단 끝4번 줄은 4칸이라 바깥 덩어리에 들어, 바깥 한 바퀴에 한 번씩 모두 두 번 실행됩니다. 같은 줄을 다르게 들이면 실행 횟수가 이렇게 바뀝니다.
| 4번 줄의 들여쓰기 | 드는 덩어리 | 실행 횟수 |
|---|---|---|
| 0칸 | 없음(반복 밖) | 1 |
| 4칸 | 바깥 반복 | 2 |
| 8칸 | 안쪽 반복 | 6 |
예제
메뉴와 컵 크기의 모든 짝
커피와 라테 두 메뉴에 S·M·L 세 크기가 있습니다. 메뉴 첫 글자와 크기를 짝지어, 주문할 수 있는 가짓수를 모두 찍습니다.
for menu in '커라':
for size in 'SML':
print(menu + size)
커S
커M
커L
라S
라M
라L바깥 메뉴 하나마다 안쪽 크기 셋이 다 돌아 2 × 3 = 6가지입니다. menu + size는 두 글자를 이어 붙입니다.
들여쓰기 칸 수가 섞이면
for i in range(2, 4):
for j in range(1, 4):
print(i, j)
print(i)
File "main.py", line 4 print(i) ^IndentationError: unindent does not match any outer indentation level맨 아래 줄부터 읽습니다. IndentationError는 들여쓰기 오류이고, '들여쓰기를 줄였는데 바깥 어느 줄의 들여쓰기와도 맞지 않는다'는 뜻입니다.
4번 줄은 6칸이라 바깥 덩어리(4칸)에도, 반복 밖(0칸)에도 맞지 않습니다. ^는 줄 끝을 가리키지만 고칠 곳은 줄 앞의 빈칸입니다. 4칸이나 0칸으로 맞춥니다.
자주 틀리는 곳
틀린 생각: 안쪽 반복과 바깥 반복이 번갈아 한 번씩 돈다.
두 for 줄이 위아래로 붙어 있어서, 2 1 2 다음에 3 2 6처럼 i와 j가 함께 한 걸음씩 나아갈 것 같습니다.
실제로는 1절 실행 결과처럼 2 1 2 다음에 2 2 4가 찍힙니다.
맞는 규칙: 안쪽 반복은 바깥 덩어리 안의 줄이라, 바깥 한 바퀴마다 처음부터 다시 돕니다. 안쪽 반복이 중간에 끝나지 않으면 끝까지 다 돌아, 안쪽 덩어리의 실행 횟수는 바깥 바퀴 × 안쪽 바퀴입니다(규칙 4).
틀린 생각: 안쪽과 바깥 반복은 같은 이름을 써도 서로 섞이지 않는다.
for 줄마다 이름을 따로 적으니, 반복마다 자기 이름이 있을 것 같습니다. 그러나 이름 i는 하나뿐입니다.
2단 코드(for i in range(1, 4): 다음 print(2 * i))를 바깥 반복으로 감싸 2단과 3단을 찍으려 했습니다. 바깥 이름도 늘 쓰던 i로 하고, 2를 i로 바꿨습니다.
for i in range(2, 4):
for i in range(1, 4):
print(i * i)
1
4
9
1
4
9안쪽 for가 같은 이름 i를 1, 2, 3에 다시 붙여서, 바깥 반복이 붙여 둔 2·3과의 연결이 끊겼습니다. 그래서 2단·3단 대신 1, 4, 9가 두 번 찍힙니다. 바깥 반복은 range(2, 4)에서 다음 수를 꺼내 다시 붙이므로 두 바퀴는 그대로 돕니다.
맞는 규칙: 이름 하나는 한 번에 값 하나만 가리키므로, 안쪽 반복이 같은 이름을 다시 붙이면 바깥 반복이 붙여 둔 값은 그 이름으로 볼 수 없습니다. 안쪽 반복의 이름은 j처럼 바깥과 다르게 씁니다(규칙 3).
틀린 생각: 안쪽 반복 아래에 쓴 줄은 몇 칸을 들이든 안쪽 반복이 끝난 뒤에 실행된다.
줄이 안쪽 for 덩어리보다 아래에 있으니, 안쪽 반복이 끝난 뒤 한 번 실행될 것처럼 보입니다. 그 줄이 어느 덩어리에 드는지는 자리가 아니라 들여쓰기가 정합니다.
3절의 print(f'{i}단 끝')을 8칸 들이면 안쪽 덩어리에 들어, 3절 표처럼 곱 하나마다 따라 찍혀 여섯 번 나옵니다.
맞는 규칙: 줄은 들여쓰기 칸 수로 정해지는 덩어리에 들고, 그 덩어리가 도는 만큼 실행됩니다. 그 줄이 몇 번 실행되어야 하는지 먼저 정하고 칸 수를 맞춥니다(규칙 4).
확인 문제
1. 1절 코드의 안쪽 range(1, 4)를 range(1, 3)으로 바꿨습니다. 무엇이 찍히는지 마지막 줄까지 글자 그대로 쓰세요.
for i in range(2, 4):
for j in range(1, 3):
print(i, j, i * j)
힌트
range(1, 3)이 내주는 수부터 적고, 바깥 i가 2일 때와 3일 때를 차례로 따라갑니다.
정답
2 1 2
2 2 4
3 1 3
3 2 6안쪽이 1, 2 두 바퀴라 모두 2 × 2 = 4줄입니다.
흔한 오답은 2 3 6과 3 3 9까지 쓰는 것입니다. 끝 수 3도 든다고 본 것입니다(끝도 든다는 생각). range는 끝 수를 넣지 않습니다.
2. 2단부터 4단까지, 1·2·3을 곱해 찍으려 합니다. 빈칸 둘을 채우고, 3번 줄이 모두 몇 번 실행되는지도 쓰세요.
for i in range(___, ___):
for j in range(1, 4):
print(i, j, i * j)
힌트
바깥 i가 2, 3, 4를 내주면 됩니다. 1절 코드의 range(2, 4)가 내주는 수와 견줘 봅니다.
정답
차례로 2와 5이고, 3번 줄은 3 × 3 = 9번 실행됩니다.
for i in range(2, 5):
for j in range(1, 4):
print(i, j, i * j)2 1 2
2 2 4
2 3 6
3 1 3
3 2 6
3 3 9
4 1 4
4 2 8
4 3 12흔한 오답은 range(2, 4)입니다. 마지막 단 4를 끝 자리에 써서 끝 수도 든다고 본 것입니다(끝도 든다는 생각). 끝 수는 넣지 않아 4단이 빠집니다.
3. 요일마다 1교시와 2교시를 차례로 찍는 시간표 코드입니다. 줄마다 찍히는 값을 고르세요.
for day in '월화':
for period in range(1, 3):
print(day, period)
- ①
월 1,월 2,화 1,화 2 - ②
월 1,화 2 - ③
월 1,화 1,월 2,화 2
힌트
어느 for가 바깥이고 어느 것이 안쪽인지 들여쓰기로 먼저 가립니다. 그다음 바깥 첫 바퀴를 끝까지 따라갑니다.
정답
월 1
월 2
화 1
화 2①입니다. 바깥이 '월'을 꺼낸 동안 안쪽이 1, 2를 다 돌고, 그다음에 '화'로 넘어갑니다.
- ② 안쪽과 바깥이 번갈아 한 번씩 돈다고 봤습니다. 바깥 한 바퀴 안에서 안쪽은 끝까지 돕니다.
- ③ 안쪽과 바깥을 뒤바꿔 읽었습니다. 바깥 반복은 1번 줄의
for day이고, 덩어리 안의for period가 바깥 한 바퀴마다 다 돕니다.
아래 1절 코드의 2번 줄 range(1, 4)를 range(1, 2)로 바꾸면 찍히는 줄을 모두 봅니다.
처음 펼 때 약 6MB를 받습니다.컴퓨터에서 해 보기
파이썬 3.14. Tab을 누르면 다음 단추나 칸으로 갑니다. 들여쓰기는 빈칸 네 개입니다. Enter는 앞줄 들여쓰기를 잇고, 콜론 뒤에서는 한 단계 더 들여 씁니다. Ctrl+Enter로 실행합니다.
'실행'을 누르면 여기에 나옵니다.
여기서 쓴 코드 · 내 예상은 저장하지도 운영자에게 보내지도 않습니다. 페이지를 새로 열면 처음 코드입니다.