제어 흐름
지금까지 작성한 모든 프로그램은 항상 같은 방식으로 실행됩니다. 위에서 아래로, 한 줄씩. 간단한 스크립트에는 충분하지만, 실제 프로그램은 결정을 내리고 작업을 반복해야 합니다. 퀴즈는 답이 맞는지 확인해야 하고, 게임은 플레이어가 이기거나 질 때까지 계속 실행되어야 합니다. 이 장에서는 프로그램을 분기하고 반복하는 방법을 배웁니다.
비교
결정을 내리려면 먼저 값들을 비교해야 합니다. 비교 연산자는 True 또는 False를 반환합니다. 초반에 제대로 알아야 할 가장 중요한 것: =는 값을 할당하고, ==는 두 값이 같은지 확인합니다. 이 둘을 혼동하는 것은 가장 흔한 초보자 실수 중 하나입니다.
5 > 3 # True
5 < 3 # False
5 == 5 # True (주의: 이중 등호; = 는 할당, == 는 비교)
5 != 3 # True ("같지 않음")
5 >= 5 # True ("크거나 같음")
5 <= 4 # False ("작거나 같음")=와 ==의 구분은 거의 모든 사람이 초반에 헷갈립니다. 할당(=)은 값을 저장하고, 비교(==)는 두 값이 같은지 확인합니다.
문자열도 비교할 수 있습니다. Python은 사전식으로 문자 단위로 문자열을 비교하며, 이에 대한 자세한 내용은 문자열 장에서 다룹니다:
"apple" == "apple" # True
"apple" < "banana" # True (a가 b보다 먼저 옴)
"apple" == "Apple" # False (대소문자 구분)=는 값을 저장하고, ==는 두 값이 같은지 확인합니다. 피곤할 때 여전히 확인하고 두 배로 확인하므로, 혼동한다면 좋은 회사에 있는 것입니다. 비교는 True 또는 False를 반환하고, 문자열에도 작동합니다. 조건 결합하기
and, or, not은 비교를 결합합니다. and는 양쪽이 모두 참이어야 합니다. or은 한쪽이라도 참이면 됩니다. not은 결과를 뒤집습니다. 이들은 "점수가 통과하고 사용자가 활성 상태"와 같은 실제 조건을 표현하게 해줍니다.
age = 25
score = 88
age >= 18 and score >= 80 # True (둘 다 참이어야 함)
age < 18 or score >= 80 # True (하나라도 참이면 됨)
not age >= 18 # False (결과 뒤집기)and는 양쪽을 요구합니다. or은 한쪽이라도 요구합니다. not은 반대로 뒤집습니다.
and는 양쪽이 참이길 원하고, or은 한쪽이라도 참이길 원하며, not은 답을 뒤집습니다. 이것이 "점수가 통과하고 사용자가 활성"과 같은 대부분의 실제 조건을 작성할 때 필요한 것입니다. Python은 답을 알 순간 체크를 멈춥니다. 참과 거짓
Python의 모든 값은 불린 해석을 가지고 있으며, True 또는 False가 아니어도 그렇습니다. 빈 문자열, 0, 빈 리스트, None은 모두 조건에서 False처럼 동작합니다. 다른 모든 것은 True처럼 동작합니다. 이것은 if results: 리스트가 비어있지 않은지 확인하는 것을 if len(results) > 0:을 쓰지 않고도 확인할 수 있다는 의미입니다.
# 이 모든 것은 조건에서 False처럼 동작합니다:
False, 0, 0.0, "", [], {}, (), None
# 다른 모든 것은 True처럼 동작합니다이것은 if results:가 "리스트가 비어있지 않다면"의 자연스러운 방법이고, if name:은 문자열에 내용이 있는지 확인한다는 의미입니다.
False로 간주됩니다: 0, "", [], None. 다른 모든 것은 True로 간주됩니다. 이것이 if results:가 "리스트에 뭔가 있다면"으로 그렇게 깔끔하게 읽히는 이유이며, len()이 필요 없습니다. if / elif / else
if 문은 조건이 True일 때만 코드 블록을 실행합니다. elif는 첫 번째 조건이 거짓이면 더 많은 조건을 확인합니다. else는 어떤 조건도 일치하지 않은 모든 것을 처리합니다. Python은 중괄호가 아니라 들여쓰기를 사용해서 각 블록에 속하는 것을 정의합니다.
score = 87
if score >= 90:
print("A 학점")
elif score >= 80:
print("B 학점")
elif score >= 70:
print("C 학점")
else:
print("C 미만")규칙:
if는 필수이며 항상 먼저 옵니다elif("else if"의 약자)는 선택 사항이며 필요한 만큼 많이 가질 수 있습니다else는 선택 사항이고 일치하지 않은 모든 것을 처리하며 마지막에 옵니다- Python은 들여쓰기(4개 공백)를 사용해서 각 블록에 속하는 것을 표시합니다. 중괄호는 없습니다
들여쓰기는 선택 사항이거나 미적인 것이 아닙니다. Python은 이를 사용해서 구조를 정의합니다. 일관성 없는 들여쓰기는 구문 오류입니다.
if는 조건이 참일 때 블록을 실행하고, elif는 더 많은 경우를 확인하며, else는 나머지를 처리합니다. 첫 번째 일치만 실행됩니다. 초반에 나를 당황하게 한 것: 들여쓰기는 Python에 각 블록이 어디서 시작하고 끝나는지를 알려주므로, 일관성 있게 유지하세요. 한 줄 조건
간단한 예/아니오 할당의 경우, Python은 삼항식이라는 컴팩트한 한 줄 형식을 가지고 있습니다: 참이면_값 if 조건 else 거짓이면_값. 논리가 작고 문장처럼 읽힐 때만 사용하세요.
label = "통과" if score >= 50 else "불통과"이것은 삼항식이며, 문장처럼 읽힙니다. 논리가 작을 때 사용하세요. elif를 포함하는 어떤 것이든 전체 버전을 작성하세요.
참이면_값 if 조건 else 거짓이면_값은 한 줄에서 두 값 중 하나를 선택합니다. "통과" if score >= 50 else "불통과"처럼 깔끔한 작은 선택을 위해 훌륭합니다. 논리가 그 이상으로 커지는 순간, 일반 if 블록으로 돌아가면 더 잘 읽힐 것입니다. while 반복문
while 반복문은 조건이 True인 동안 블록을 반복합니다. 반복문이 몇 번 실행되어야 하는지 미리 모를 때 사용하세요. 예를 들어 유효한 입력을 기다리거나 작업이 성공할 때까지 재시도합니다.
lives = 3
while lives > 0:
print(f"남은 생명: {lives}")
lives -= 1
print("게임 종료")while은 반복문이 몇 번 실행될지 미리 모를 때 가장 좋습니다. 미리 알거나 컬렉션을 순회 중이면 for가 더 깔끔합니다.
while은 조건이 참인 동안 블록을 계속 실행합니다. 유효한 입력을 기다릴 때처럼 몇 라운드가 필요한지 모를 때 사용하세요. 반복문 안에서 뭔가가 종료로 이동하는지 확인하세요. 그렇지 않으면 영원히 실행되고, 이 실수는 한 번 이상 저질렀습니다. break와 continue
break는 남은 반복이 얼마나 있든 반복문을 즉시 빠져나갑니다. continue는 현재 반복의 나머지를 건너뛰고 조건 확인으로 돌아갑니다. 둘 다 자신을 감싼 가장 가까운 반복문에만 영향을 줍니다.
break는 반복문을 즉시 빠져나갑니다:
target = 5
num = 0
while True:
num += 1
if num == target:
print(f"{target}를 찾음")
break # 반복문 멈추기while True:와 break는 종료 조건이 복잡하거나 반복문 본문의 끝에서 발생해야 할 때 유효하고 일반적인 패턴입니다.
continue는 현재 반복의 나머지를 건너뛰고 조건 확인으로 돌아갑니다:
num = 0
while num < 10:
num += 1
if num % 2 == 0:
continue # 짝수 건너뛰기
print(num) # 홀수만 출력: 1, 3, 5, 7, 9break는 반복문에서 바로 뛰어나가고, continue는 이 라운드의 나머지를 건너뛰고 조건 확인으로 돌아갑니다. 둘 다 자신들이 직접 안에 있는 반복문만 건드립니다. while True:는 처음에는 이상해 보이지만, 뭔가 발생할 때까지 반복하는 일반적인 방법입니다. for 반복문
for 반복문은 수열을 한 번에 하나씩 항목으로 이동합니다: 리스트, 문자열, 숫자 범위. for 뒤에 이름을 지은 변수는 각 항목을 차례로 받습니다. 카운터를 관리하거나 길이를 직접 확인할 필요가 없습니다.
players = ["민지", "준호", "지은"]
for player in players:
print(f"안녕하세요, {player}!")for 반복문은 문자열(문자 단위로 반복)과 다른 수열 타입에도 작동합니다.
for item in sequence는 차례로 각 항목을 순회하며, 카운터나 길이를 직접 건드리지 않습니다. 리스트, 문자열, 범위, 많은 것에서 작동합니다. 다른 언어에서 오면, 인덱스를 관리하지 않는 것이 처음에는 낄낄거렸고, 그 다음에는 선물 같았습니다. range()
range()는 반복할 숫자 수열을 생성합니다. range(5)는 0, 1, 2, 3, 4를 줍니다. 시작, 끝, 스텝 크기를 제어할 수 있습니다. 반복문이 특정 횟수 실행되기를 원할 때 사용하세요.
for i in range(5):
print(i) # 0, 1, 2, 3, 4range()에는 세 가지 형태가 있습니다:
| 호출 | 생성되는 것 |
|---|---|
range(5) | 0, 1, 2, 3, 4 |
range(2, 6) | 2, 3, 4, 5 |
range(0, 10, 2) | 0, 2, 4, 6, 8 (2씩 증가) |
range(5, 0, -1) | 5, 4, 3, 2, 1 (역순) |
range()는 리스트를 생성하지 않습니다. 한 번에 하나씩 숫자를 생성하며, 매우 큰 범위에서도 효율적입니다.
range(5)는 0, 1, 2, 3, 4를 주며, 시작, 멈춤, 단계를 설정할 수 있습니다. 반복문이 설정된 횟수를 실행하기를 원할 때 사용하세요. 리스트를 빌드하지 않으며, 라운드마다 하나씩 숫자를 건네줍니다. enumerate()
enumerate()는 반복 중에 인덱스와 값을 모두 제공하므로, 카운터를 따로 추적할 필요가 없습니다. i, player 부분은 각 반복에서 자동으로 값의 쌍을 받습니다.
players = ["민지", "준호", "지은"]
for i, player in enumerate(players):
print(f"{i + 1}. {player}")
# 1. 민지
# 2. 준호
# 3. 지은i, player 구문은 언팩이라고 불립니다. Python은 (index, value) 쌍을 자동으로 두 이름으로 분할합니다.
기본적으로 enumerate()는 0부터 시작합니다. 이를 바꾸려면 시작 값을 전달하세요:
for i, player in enumerate(players, start=1):
print(f"{i}. {player}") # 1부터 시작enumerate()는 위치와 값을 함께 건네주므로, 동기화된 별도 카운터가 없습니다. i, player 부분은 한 번에 둘 다를 캡처합니다. 누군가 읽기 위해 리스트를 번호 매기고 있을 때 start=1을 전달하세요. 중첩된 반복문
한 반복문을 다른 반복문 안에 넣을 수 있습니다. 바깥쪽 반복문의 각 단일 반복에 대해 안쪽 반복문이 완전히 실행됩니다. 이것은 그리드, 조합, 또는 두 단계 구조를 가진 모든 데이터를 처리하는 방법입니다.
rows = [1, 2, 3]
cols = ["A", "B"]
for row in rows:
for col in cols:
print(f"{col}{row}", end=" ")
print() # 각 행 후 새로운 줄
# A1 B1
# A2 B2
# A3 B3중첩된 반복문 내부의 break와 continue는 가장 안쪽 반복문에만 영향을 줍니다.
break와 continue는 내부에 있는 반복문에만 작동합니다. 반복문-else
Python 반복문은 break에 도달하지 않고 반복문이 완료된 경우에만 실행되는 else 절을 가질 수 있습니다. 흔하게 사용되지는 않지만, "리스트를 검색하고 아무것도 찾지 못하면 이것을 하세요"를 작성하는 가장 깔끔한 방법입니다.
target = "수호"
names = ["민지", "준호", "지은"]
for name in names:
if name == target:
print(f"{target}을 찾음")
break
else:
print(f"{target}은 리스트에 없음") # break가 실행되지 않아서 실행됨break가 실행되면, else는 건너뜁니다. 반복문이 수열을 소비하면, else는 실행됩니다. 이것은 틈새 패턴이지만 플래그 변수보다 깔끔합니다.
break 없이 끝날 때만 실행되는 else를 가질 수 있습니다. 자주 사용되지 않지만, "이 리스트를 검색하고 아무것도 일치하지 않으면 이것을 하세요"라고 말할 가장 깔끔한 방법입니다. 이름이 좀 오해의 소지가 있으므로, 짧은 주석이 도움이 됩니다. 정렬
sorted()는 새 정렬된 리스트를 반환하고 원본은 바꾸지 않습니다. .sort()는 제자리에서 리스트를 정렬하고 None을 반환합니다. key= 인자를 사용하면 원시 값이 아닌 다른 항목으로 정렬할 수 있습니다. 예를 들어, 이름을 대소문자를 구분하지 않고 정렬하거나 플레이어 튜플을 점수로 정렬합니다.
scores = [87, 42, 96, 55, 71]
ranked = sorted(scores) # [42, 55, 71, 87, 96] (새 리스트)
scores.sort() # 원본 리스트를 정렬하며, None을 반환
scores.sort(reverse=True) # [96, 87, 71, 55, 42]둘 다 key= 인자를 허용합니다: 비교 전에 각 항목에 적용되는 함수:
names = ["지은", "민지", "준호"]
sorted(names, key=str.lower) # 대소문자 구분 없이 정렬
players = [("민지", 87), ("준호", 96), ("지은", 55)]
sorted(players, key=lambda p: p[1]) # 점수로 정렬lambda p: p[1]은 한 줄 함수입니다: 플레이어 튜플을 받고 점수를 반환합니다. 람다는 람다와 컴프리헨션 장에서 충분히 다룹니다.
간단한 경우에는 sorted()를 사용하세요. 제자리 수정을 원하는 리스트의 경우 .sort()를 사용하세요.
sorted()는 새 정렬된 리스트를 건네주고 원본은 혼자 두며, .sort()는 리스트 자체를 다시 정렬하고 None을 반환합니다. 원시 값 이외의 다른 항목으로 정렬하려면 key=을 전달합니다. 확실하지 않으면 sorted()를 사용하세요. 안전합니다. 실제로
점수를 반복하고, 합계를 누적하고, 통과 학점을 세고, 요약을 출력합니다:
raw_scores = [87, 42, 96, 55, 71, 63]
total = 0
passing = 0
for score in raw_scores:
total += score
if score >= 60:
passing += 1
average = total / len(raw_scores)
print(f"평균: {average:.1f}")
print(f"통과: {passing}/{len(raw_scores)}")
print(f"최고 점수: {sorted(raw_scores, reverse=True)[0]}")
