Python 강좌 : 제 15강 - 반복문
반복문(for, while)
반복문이란 특정한 부분의 코드가 반복적으로 수행되는 구문입니다.
특정 조건을 만족할 때까지 반복해서 수행하게 되며, 루프(Loop)라고도 합니다.
목록에 담긴 1,000개의 값을 모두 출력하거나, 사용자가 올바른 값을 입력할 때까지 다시 묻는 작업을 같은 코드의 복사·붙여넣기로 작성할 수는 없습니다. 반복문은 한 번 작성한 코드를 여러 번 실행하게 해주며, 앞의 제 14강에서 배운 조건문과 함께 알고리즘의 흐름을 구성하는 핵심 요소입니다.
Python에서는 for와 while을 이용하여 반복문을 구성할 수 있습니다.
for 문은 정해진 데이터의 요소를 하나씩 꺼내며 반복하고, while 문은 조건이 참인 동안 반복합니다. 즉, 반복할 대상이 있으면 for, 언제 끝날지 조건으로만 알 수 있으면 while을 사용합니다.
for 문의 반복 대상으로 List, Tuple, Str, Range, Dictionary, Set 등 반복 가능한(Iterable) 객체를 사용할 수 있습니다.
이번 강좌에서는 for와 while의 실행 흐름을 한 회차씩 따라가 보고, range, enumerate, zip, else, break, continue, 할당 표현식(:=)을 차례대로 다룹니다.
for 문
for 문은 반복 가능한 객체에서 요소를 처음부터 끝까지 하나씩 꺼내 변수에 할당한 뒤, 들여쓴 블록을 실행하는 과정을 반복합니다.
L = ["일","이","삼"]
T = (1, 2, 3)
S = "윤대희"
total = 0
for i in L:
print(i)
for i in T:
print(i)
for i in S:
print(i)
for i in range(1,10):
total += i
print (total)- 결과
- 일
이
삼
1
2
3
윤
대
희
45
for 원소 in 반복값:을 이용하여 반복문을 실행시킬 수 있습니다.
반복값 내부에 있는 요소를 순차적으로 꺼내 사용하게 됩니다.
반복값에는 리스트, 튜플, 문자열, 범위 값 등을 이용할 수 있습니다.
문자열을 반복하면 한 글자씩 꺼내므로, "윤대희"는 윤, 대, 희 세 번 반복됩니다.
range(1, 10)은 1부터 9까지의 정수를 의미하므로, 예제의 total은 1 + 2 + ... + 9 = 45가 됩니다.
마지막 print(total)은 for와 같은 깊이로 작성되어 반복 블록에 포함되지 않으므로, 반복이 모두 끝난 뒤 한 번만 실행됩니다. 들여쓰기를 한 단계 더 하면 매 반복마다 출력됩니다.
total = 0
for i in range(1, 5):
total += i
print("i =", i, "/ total =", total)
print("반복 후 i =", i)- 결과
- i = 1 / total = 1
i = 2 / total = 3
i = 3 / total = 6
i = 4 / total = 10
반복 후 i = 4
반복 변수가 매 회 어떻게 바뀌는지 출력해 확인하는 예제입니다.
- 1회차 :
range(1, 5)에서 첫 값1을 꺼내i에 할당합니다.total은0 + 1 = 1이 됩니다. - 2회차 : 다음 값
2를 꺼내i에 새로 할당합니다.total은1 + 2 = 3이 됩니다. - 3회차와 4회차도 같은 방식으로
i가3,4가 되며total은6,10이 됩니다. - 더 꺼낼 값이 없으므로 반복을 종료하고 다음 줄로 이동합니다.
total처럼 반복 바깥에서 만든 변수는 매 회차의 결과가 누적되지만, i는 매 회차 시작 시 다음 요소로 덮어쓰여 집니다.
반복이 끝난 뒤에도 i에는 마지막으로 할당된 값 4가 남아 있습니다. 단, 반복 대상이 비어 있어 한 번도 반복하지 않았다면 i는 만들어지지 않습니다.
L = ["일", "이", "삼"]
it = iter(L)
print(next(it))
print(next(it))
print(next(it))
print(next(it))- 결과
- 일
이
삼
StopIteration
for 문이 내부적으로 요소를 꺼내는 원리를 직접 재현한 예제입니다.
for 문은 먼저 iter(반복값)으로 반복자(Iterator)를 만들고, 매 회차마다 next(반복자)를 호출해 다음 요소를 꺼냅니다. 꺼낼 요소가 없으면 next()는 StopIteration 예외를 발생시키며, for 문은 이 예외를 신호로 삼아 조용히 반복을 종료합니다.
직접 next()를 호출하면 이 예외가 그대로 드러나므로, 네 번째 호출에서 StopIteration이 발생합니다.
이 원리 덕분에 for 문은 목록, 문자열, 사전, 파일 등 iter()를 지원하는 모든 객체를 같은 문법으로 반복할 수 있습니다.
for i in range(3):
print(i)
i = 10- 결과
- 0
1
2
반복 블록 안에서 반복 변수에 다른 값을 할당해도 다음 회차에는 영향을 주지 않습니다.
i = 10은 이번 회차의 i만 바꿀 뿐이며, 다음 회차가 시작되면 for 문이 반복자에서 다음 값을 꺼내 i에 다시 할당하기 때문입니다. 다른 언어의 for (i = 0; ...; i++)처럼 변수를 조작해 반복 횟수를 바꿀 수는 없습니다.
L = [1, 2, 2, 3]
for x in L:
if x == 2:
L.remove(x)
print(L)- 결과
- [1, 2, 3]
반복 중인 목록을 직접 수정하면 예상과 다른 결과가 나옵니다. 2를 모두 지우려 했지만 하나가 남았습니다.
목록의 반복자는 내부적으로 현재 위치(인덱스)를 기억하며 하나씩 전진합니다. 인덱스 1의 2를 지우면 뒤의 요소들이 한 칸씩 앞으로 당겨져 두 번째 2가 인덱스 1로 이동하는데, 반복자는 다음 회차에 인덱스 2(이제 3)를 꺼내므로 두 번째 2를 건너뛰게 됩니다.
반복하면서 요소를 제거해야 한다면 for x in L[:]:처럼 복사본을 반복하거나, [x for x in L if x != 2]처럼 조건에 맞는 요소만 모아 새 목록을 만듭니다. 두 방법 모두 [1, 3]이 됩니다.
print(list(range(5)))
print(list(range(2, 10, 3)))
print(list(range(5, 0, -1)))- 결과
- [0, 1, 2, 3, 4]
[2, 5, 8]
[5, 4, 3, 2, 1]
range(end)는 0부터 end-1까지, range(start, end)는 start부터 end-1까지의 정수를 생성합니다.
range(start, end, step)은 step 간격으로 정수를 생성하며, step이 음수이면 감소하는 방향으로 생성합니다.
end는 포함되지 않는다는 점에 주의합니다. 덕분에 range(n)은 정확히 n번 반복하며, range(len(L))은 목록 L의 인덱스 0부터 len(L)-1까지와 정확히 일치합니다.
range(2, 10, 3)은 2에서 시작해 3씩 더하며 10보다 작은 동안 생성하므로 2, 5, 8이 됩니다.
-
Tip :
range()는 값을 미리 모두 만들어두지 않으므로, 내용을 확인하려면list()로 변환해야 합니다. -
Tip :
range(5, 0)처럼step없이 시작 값이 끝 값보다 크면 빈 범위가 되어 반복이 한 번도 실행되지 않습니다. 감소하려면 반드시 음수step을 지정합니다.
range 객체는 시작·끝·간격만 저장하고 필요할 때 값을 계산하므로, range(10**9)처럼 큰 범위도 메모리를 거의 사용하지 않습니다. 또한 len(range(0, 100, 7))은 15, range(10)[3]은 3처럼 길이와 인덱싱, in 검사도 값을 만들지 않고 계산합니다.
enumerate & zip
반복할 때 요소뿐만 아니라 몇 번째 요소인지 알아야 하거나, 두 개 이상의 목록을 나란히 반복해야 하는 경우가 많습니다. enumerate와 zip은 이런 작업을 인덱스 계산 없이 처리해줍니다.
L = ["일", "이", "삼"]
for i, v in enumerate(L):
print(i, v)
for i, v in enumerate(L, start=1):
print(i, v)
names = ["윤대희", "홍길동"]
ages = [30, 25]
for name, age in zip(names, ages):
print(name, age)- 결과
- 0 일
1 이
2 삼
1 일
2 이
3 삼
윤대희 30
홍길동 25
enumerate(반복값)을 사용하면 요소와 함께 순번(index)을 (index, 요소) 튜플로 꺼낼 수 있습니다.
list(enumerate(L))로 확인하면 [(0, '일'), (1, '이'), (2, '삼')]입니다. for i, v in ...은 매 회차 이 튜플을 언패킹하여 첫 번째 값을 i에, 두 번째 값을 v에 할당합니다.
start 매개변수로 순번의 시작 값을 변경할 수 있습니다. 사람에게 보여줄 번호처럼 1부터 세야 할 때 사용하며, 요소 자체는 그대로입니다.
zip(반복값1, 반복값2)을 사용하면 여러 반복값의 요소를 같은 위치끼리 묶어서 꺼낼 수 있습니다. 1회차에는 ("윤대희", 30), 2회차에는 ("홍길동", 25)가 만들어져 각각 name과 age에 할당됩니다.
-
Tip :
for i in range(len(L)):후L[i]로 접근하는 방식보다enumerate()를 사용하는 것이 더 간결하고 Python다운 방식입니다. -
Tip :
zip()은 가장 짧은 반복값의 길이에 맞춰 종료됩니다. 사전은for key, value in 사전.items():처럼 반복할 수 있습니다.
names = ["윤대희", "홍길동", "이순신"]
ages = [30, 25]
print(list(zip(names, ages)))
for name, age in zip(names, ages, strict=True):
print(name, age)- 결과
- [(‘윤대희’, 30), (‘홍길동’, 25)]
윤대희 30
홍길동 25
ValueError: zip() argument 2 is shorter than argument 1
zip()은 길이가 다르면 아무 경고 없이 짧은 쪽에 맞춰 멈추므로, "이순신"이 조용히 빠졌습니다. 데이터가 누락된 것을 알아채지 못하는 원인이 될 수 있습니다.
Python 3.10 이상에서는 zip(..., strict=True)를 사용할 수 있습니다. (PEP 618) 길이가 같아야 하는 데이터를 묶을 때 지정하면, 한쪽이 먼저 끝나는 순간 ValueError를 발생시킵니다.
결과에서 보듯 앞의 두 쌍은 정상적으로 반복된 뒤, 세 번째 쌍을 만들려는 시점에 길이 차이가 발견되어 오류가 발생합니다.
for 문 - else
반복문에도 else를 붙일 수 있습니다. 조건문의 else와 이름은 같지만, 반복문의 else는 반복이 중단 없이 끝났을 때 실행된다는 점이 다릅니다.
for i in range(3):
print("i =", i)
else:
print("END")
for j in range(3):
if j == 2:
break
print("j =", j)
else:
print("END")- 결과
- i = 0
i = 1
i = 2
END
j = 0
j = 1
for else문은 for 문이 끝까지 실행되어 종료됐을 때, else 구문으로 넘어갑니다.
즉, 중간에 중단(break)되지 않고 끝까지 실행된다면 else 구문을 실행합니다.
else는 for와 같은 들여쓰기 깊이로 작성하며, 도중에 break로 중단된다면 else 구문을 실행하지 않습니다.
첫 번째 반복문은 i가 0, 1, 2를 모두 거친 뒤 꺼낼 값이 없어 정상 종료되므로 END가 출력됩니다.
두 번째 반복문은 j가 2가 되는 회차에 print보다 먼저 break를 만나 반복이 중단되므로, j = 2도 END도 출력되지 않습니다.
numbers = [3, 8, 5]
for n in numbers:
if n % 2 == 0:
print("짝수 발견 :", n)
break
else:
print("짝수 없음")- 결과
- 짝수 발견 : 8
for-else는 찾는 값이 있는지 검색할 때 유용합니다.
값을 찾으면 break로 중단하고, 끝까지 찾지 못했을 때만 else에서 처리합니다. numbers를 [3, 7, 5]로 바꾸면 짝수 없음이 출력됩니다.
for-else를 사용하지 않으면 found = False 같은 표시용 변수를 만들어 찾았을 때 True로 바꾸고, 반복 후에 다시 검사해야 합니다. else는 이 과정을 문법으로 대신해줍니다.
- Tip :
while문에도else를 사용할 수 있으며, 조건이 거짓이 되어 종료되었을 때 실행됩니다.
while 문
while 문은 반복할 대상 없이 조건만으로 반복 여부를 결정합니다. 매 회차를 시작하기 전에 조건을 검사하여 참이면 블록을 실행하고, 거짓이면 반복을 끝냅니다.
L = ["일","이","삼"]
T = (1, 2, 3)
S = "윤대희"
total = 0
i = 0
while i < len(L):
print(L[i])
i += 1
i = 0
while i < len(T):
print(T[i])
i += 1
i = 0
while i < len(S):
print(S[i])
i += 1
i = 0
while i < 10:
total += i
i += 1
print(total)- 결과
- 일
이
삼
1
2
3
윤
대
희
45
while 조건:을 이용하여 반복문을 실행시킬 수 있습니다.
조건에는 참과 거짓의 값으로 반환되어야 합니다.
참 값일 때 반복하며 거짓 값일 때 종료합니다.
for 문과 같은 결과를 얻기 위해 while 문에서는 세 가지를 직접 관리해야 합니다. 반복 변수의 초기값(i = 0), 반복 조건(i < len(L)), 변수의 변화(i += 1)입니다. 이 중 하나라도 빠지거나 잘못되면 반복 횟수가 달라집니다.
마지막 반복문은 i가 0부터 9까지 더해지므로 total은 45가 됩니다. i가 10이 되는 순간 조건 i < 10이 거짓이 되어 반복이 끝납니다.
만약, 조건이 계속 참 값으로 유지되어 거짓 값이 되지 않는다면 무한 루프(Infinite Loop)에 빠지게 되며, 강제로 종료하기 전까지 종료되지 않습니다.
- Tip : 콘솔에서 무한 루프를 강제로 종료하려면
Ctrl + C를 누릅니다.
i = 0
while i < 3:
print("i =", i, "/ 조건 :", i < 3)
i += 1
print("종료 시 i =", i, "/ 조건 :", i < 3)- 결과
- i = 0 / 조건 : True
i = 1 / 조건 : True
i = 2 / 조건 : True
종료 시 i = 3 / 조건 : False
조건이 언제 검사되는지 확인하는 예제입니다.
i가0일 때0 < 3은True이므로 블록을 실행하고i가1이 됩니다.- 블록이 끝나면 다시
while줄로 돌아가 조건을 검사합니다.1 < 3,2 < 3도 참이므로 두 번 더 실행됩니다. i가3이 된 뒤 조건을 검사하면3 < 3은False이므로 블록을 실행하지 않고 반복문 다음 줄로 이동합니다.
조건은 블록 실행 전에만 검사되며, 블록 중간에 조건이 거짓이 되어도 그 회차의 나머지 코드는 끝까지 실행됩니다. 또한 처음부터 조건이 거짓이면 블록은 한 번도 실행되지 않습니다.
total = 0
while True:
total += 7
if total > 30:
break
print(total)- 결과
- 35
while True:는 조건이 항상 참이므로 의도적인 무한 루프입니다. 종료 조건을 반복문 첫 줄이 아닌 블록 중간에서 검사해야 할 때, if와 break를 함께 사용해 빠져나옵니다.
total은 7, 14, 21, 28, 35 순서로 증가하며, 35가 되었을 때 total > 30이 참이 되어 break로 반복을 종료합니다.
사용자 입력을 올바른 값이 들어올 때까지 반복해서 받는 경우처럼, 먼저 실행한 뒤 결과를 보고 멈출지 결정하는 작업에 자주 사용합니다.
- Tip : 반복 횟수나 대상이 정해져 있다면
for, 종료 시점을 조건으로만 알 수 있다면while을 사용합니다.for문은 반복 변수를 직접 증가시킬 필요가 없어 무한 루프나 횟수 실수가 적습니다.
for문 - continue & break
반복 도중 특정 상황에서 반복을 완전히 끝내거나, 이번 회차만 건너뛰어야 하는 경우가 있습니다. 이때 break와 continue를 사용합니다.
for i in range(5):
if i == 3: break
print(i)
print("break")
for i in range(5):
if i == 3: continue
print(i)
print("continue")- 결과
- 0
1
2
break
0
1
2
4
continue
반복문은 반복 횟수 만큼 반복이 진행되거나, 특정 조건을 만족해야 반복문이 종료됩니다.
하지만, 조건문(if) 등을 통해 반복 도중 특정 조건이 만족한다면 모든 반복을 완료하지 않아도 반복을 종료할 수 있습니다.
조건문과 break, continue 등을 이용해 특정 조건을 만족할 때 종료하거나 건너뛰게 할 수 있습니다.
break는 i가 3일 때 반복문 전체를 종료하므로 0, 1, 2만 출력됩니다.
continue는 i가 3일 때 이번 반복의 나머지 부분만 건너뛰고 다음 반복을 계속하므로 3을 제외한 0, 1, 2, 4가 출력됩니다.
두 구문의 실행 위치 이동을 정리하면 다음과 같습니다.
break: 즉시 반복문을 빠져나가 반복문 다음 줄(print("break"))로 이동합니다. 남은4는 꺼내지 않습니다.-
continue: 즉시 반복문의 맨 위로 돌아가 다음 요소(4)를 꺼냅니다. 같은 회차의print(i)만 실행되지 않습니다. - Tip :
if i == 3: break처럼 한 줄로 작성할 수 있지만, 블록이 길어지면 줄을 나누어 작성하는 것이 읽기 쉽습니다.
for i in range(3):
for j in range(3):
if j == 1:
break
print(i, j)- 결과
- 0 0
1 0
2 0
반복문이 중첩된 경우 break와 continue는 가장 가까운(안쪽) 반복문에만 적용됩니다.
예제에서 break는 안쪽 for j 반복문만 종료하며, 바깥쪽 for i 반복문은 계속 실행됩니다.
바깥 반복의 각 회차(i가 0, 1, 2)마다 안쪽 반복이 처음부터 다시 시작되어 j가 0일 때 출력하고, j가 1이 되면 안쪽 반복만 끝납니다. 그래서 j가 0인 줄만 세 번 출력됩니다.
found = False
for i in range(3):
for j in range(3):
if i * j == 2:
found = True
break
if found:
break
print(i, j)- 결과
- 1 2
Python에는 바깥 반복문까지 한 번에 빠져나가는 문법이 없으므로, 중첩 반복을 모두 끝내려면 표시용 변수를 사용합니다.
i가 1, j가 2일 때 i * j == 2가 참이 되어 found를 True로 바꾸고 안쪽 반복을 빠져나옵니다. 바깥 반복은 found를 확인해 다시 break하므로 두 반복문이 모두 종료되며, i와 j에는 마지막 값이 남아 있습니다.
- Tip : 중첩 반복을 함수로 분리하고 찾았을 때
return하면 표시용 변수 없이 모든 반복을 한 번에 끝낼 수 있습니다. 함수는 이후 강좌에서 다룹니다.
while문 - continue & break
while 문에서도 break와 continue를 똑같이 사용할 수 있습니다. 다만 반복 변수를 직접 변경하므로, 변경 코드의 위치가 결과에 영향을 준다는 점을 주의해야 합니다.
i = 0
while i < 5:
i += 1
if i == 3: break
print(i)
print("break")
i = 0
while i < 5:
i += 1
if i == 3: continue
print(i)
print("continue") - 결과
- 1
2
break
1
2
4
5
continue
while 문도 for 문과 마찬가지로 조건문 등을 이용해 내부 반복을 수정할 수 있습니다.
조건문과 break, continue를 이용해 특정 조건을 만족할 때 종료하거나 건너뛰게 할 수 있습니다.
예제에서는 print(i)보다 먼저 i += 1을 실행하므로, 처음 출력되는 값은 0이 아닌 1입니다.
첫 번째 반복문은 i가 3이 되는 순간 break로 종료되므로 1, 2만 출력됩니다.
두 번째 반복문은 i가 3일 때만 continue로 출력을 건너뛰고, i가 5가 될 때까지 계속 반복하므로 1, 2, 4, 5가 출력됩니다.
while 문에서 continue를 만나면 블록의 남은 부분을 건너뛰고 조건 검사(while i < 5)로 돌아갑니다. i가 5일 때는 print(5)까지 실행한 뒤 조건 5 < 5가 거짓이 되어 종료됩니다.
for 문의 예제와 달리 0이 출력되지 않고 5가 출력되는 이유는 증가 연산의 위치 때문입니다.
-
Tip :
break또는continue구문을 만났을 때 종료하거나 건너뜁니다. -
Tip :
while문에서continue구문을 잘못 사용할 경우무한 루프에 빠질 수 있습니다. 예를 들어i += 1을continue아래에 작성하면,i가3일 때 증가 연산을 건너뛰게 되어i가 영원히3에 머무르게 됩니다.
n = 0
while n < 3:
n += 1
else:
print("while 정상 종료 :", n)- 결과
- while 정상 종료 : 3
while-else도 for-else와 같은 규칙을 따릅니다. 조건 n < 3이 거짓이 되어 반복이 자연스럽게 끝나면 else가 실행되며, break로 빠져나간 경우에는 실행되지 않습니다.
할당 표현식(:=)
while 문에서는 “값을 구한다 → 그 값으로 조건을 검사한다 → 블록에서 그 값을 사용한다”는 흐름이 자주 등장합니다. 할당 표현식은 값을 구하는 동시에 조건에 사용할 수 있게 해줍니다.
data = [5, 3, 8, 0, 7]
i = 0
while (value := data[i]) != 0:
print(value)
i += 1
print("종료 위치 :", i)- 결과
- 5
3
8
종료 위치 : 3
Python 3.8 이상에서는 할당 표현식(Assignment Expression) 연산자 :=를 사용할 수 있습니다. (PEP 572)
모양 때문에 바다코끼리 연산자(Walrus Operator)라고도 부르며, 값을 변수에 할당하는 동시에 그 값을 식의 결과로 사용합니다.
예제에서는 data[i]를 value에 할당하면서 동시에 0과 비교합니다. 0을 만나면 조건이 거짓이 되어 반복을 종료합니다.
매 회차의 흐름은 다음과 같습니다. i가 0일 때 value에 5가 할당되고 5 != 0이 참이므로 5를 출력한 뒤 i가 1이 됩니다. 같은 방식으로 3, 8을 출력하고, i가 3일 때 value에 0이 할당되어 조건이 거짓이 되므로 반복이 끝납니다. 마지막 7은 읽지 않습니다.
할당 표현식을 사용하지 않으면 반복문 앞과 반복문 끝에서 같은 할당 코드를 두 번 작성하거나, while True:와 break를 함께 사용해야 합니다.
-
Tip :
:=를 조건식 안에서 사용할 때는(value := data[i]) != 0처럼 소괄호로 감싸야 의도한 순서로 연산됩니다. -
Tip : 일반적인 할당문에서는
=를 사용하며,:=는while이나if의 조건식처럼 식 안에서 할당이 필요할 때 사용합니다.
정리
| 이름 | 설명 | 반환값/특징 |
|---|---|---|
for 변수 in 반복값: |
반복값의 요소를 하나씩 꺼내 반복 | 내부적으로 iter()와 next() 사용, 요소가 없으면 종료 |
while 조건: |
조건이 참인 동안 반복 | 매 회차 시작 전에 조건 검사, 변수 변경을 직접 관리 |
range(start, end, step) |
정수 범위 생성 | end는 포함하지 않음, 값을 미리 만들지 않음 |
enumerate(반복값, start=0) |
순번과 요소를 함께 반복 | (index, 요소) 튜플 |
zip(반복값1, 반복값2, strict=False) |
같은 위치의 요소끼리 묶어 반복 | 짧은 쪽에 맞춤, strict=True는 Python 3.10 이상 |
break |
반복문 즉시 종료 | 가장 가까운 반복문에만 적용, else 실행 안 함 |
continue |
이번 회차의 나머지를 건너뜀 | 다음 요소(for) 또는 조건 검사(while)로 이동 |
for/while - else |
중단 없이 끝났을 때 실행 | 검색 실패 처리에 유용 |
:= |
할당 표현식 | Python 3.8 이상, 할당한 값을 식의 결과로 사용 |
반복문을 읽을 때는 매 회차 반복 변수가 어떤 값을 갖는지, 그리고 break와 continue를 만났을 때 실행이 어디로 이동하는지를 따라가 보는 것이 가장 확실한 방법입니다. 결과가 예상과 다르다면 반복 블록 안에 print()를 넣어 변수의 변화를 직접 확인해봅니다.
댓글 남기기