제어 흐름
지금까지 작성한 모든 프로그램은 항상 같은 방식으로 실행됩니다. 위에서 아래로, 한 줄씩 차례대로 말이죠. 간단한 스크립트에는 그것으로 충분하지만, 실제 프로그램은 결정을 내리고 작업을 반복해야 합니다. 퀴즈는 답이 맞는지 확인해야 하고, 게임은 플레이어가 이기거나 질 때까지 계속 실행되어야 합니다. 이 장에서는 프로그램을 분기시키고 반복시키는 방법을 다룹니다.
비교
결정을 내리기 전에 무언가를 비교해야 합니다. 비교 연산자는 True 또는 False를 반환합니다. 초기에 가장 중요하게 익혀야 할 것: =는 값을 할당하고, ==는 두 값이 같은지 확인합니다. 이 둘을 혼동하는 것은 초보자가 가장 흔히 저지르는 실수 중 하나입니다.
5 > 3 # True
5 < 3 # False
5 == 5 # True (참고: 두 개의 등호; =는 할당, ==는 비교)
5 != 3 # True ("같지 않음")
5 >= 5 # True ("크거나 같음")
5 <= 4 # False ("작거나 같음")=와 ==의 구분은 초기에 거의 모든 사람을 헷갈리게 합니다. 할당(=)은 값을 저장하고, 비교(==)는 두 값이 같은지 확인합니다.
문자열도 비교할 수 있습니다. 파이썬은 알파벳순으로 비교합니다:
"apple" == "apple" # True
"apple" < "banana" # True (a가 b보다 먼저)
"apple" == "Apple" # False (대소문자 구분)조건 결합하기
and, or, not은 비교를 결합합니다. and는 양쪽이 모두 참이어야 합니다. or는 적어도 한쪽이 참이어야 합니다. not은 결과를 뒤집습니다. 이를 통해 "점수가 합격선 이상 AND 사용자가 활성 상태"와 같은 실제 조건을 표현할 수 있습니다.
age = 25
score = 88
age >= 18 and score >= 80 # True (둘 다 참이어야 함)
age < 18 or score >= 80 # True (적어도 하나는 참이어야 함)
not age >= 18 # False (결과를 뒤집음)and는 양쪽 모두 필요합니다. or는 적어도 한쪽이 필요합니다. not은 반전시킵니다.
참스러움(Truthy)과 거짓스러움(Falsy)
파이썬의 모든 값은 True나 False가 아니어도 부울 해석을 가집니다. 빈 문자열, 0, 빈 리스트, None은 모두 조건에서 False처럼 동작합니다. 그 외 모든 것은 True처럼 동작합니다. 즉, if results:는 if len(results) > 0:을 쓰지 않고도 리스트가 비어있지 않은지 확인합니다.
# 이들은 모두 조건에서 False처럼 동작합니다:
False, 0, 0.0, "", [], {}, (), None
# 그 외 모든 것은 True처럼 동작합니다이는 if results:가 "리스트가 비어있지 않다면"이라고 말하는 자연스러운 방법이며, if name:은 문자열에 어떤 내용이 있는지 확인합니다.
if / elif / else
if 문은 조건이 True일 때만 코드 블록을 실행합니다. elif는 첫 번째가 거짓인 경우 확인할 추가 조건을 추가합니다. else는 어떤 조건과도 일치하지 않은 모든 경우를 잡아냅니다. 파이썬은 중괄호가 아닌 들여쓰기로 각 블록에 속한 내용을 정의합니다.
score = 87
if score >= 90:
print("A grade")
elif score >= 80:
print("B grade")
elif score >= 70:
print("C grade")
else:
print("Below C")규칙:
if는 필수이며 항상 먼저 나옵니다elif("else if"의 약자)는 선택 사항이며 필요한 만큼 가질 수 있습니다else는 선택 사항이며, 어떤 조건과도 일치하지 않은 모든 것을 처리하고, 마지막에 옵니다- 파이썬은 각 블록에 속한 내용을 표시하기 위해 들여쓰기(4개의 공백)를 사용합니다. 중괄호는 없습니다
들여쓰기는 선택 사항이거나 외관상의 문제가 아닙니다. 파이썬은 이를 구조 정의에 사용합니다. 일관성 없는 들여쓰기는 문법 오류입니다.
한 줄 조건문
간단한 예/아니오 할당의 경우, 파이썬에는 삼항식이라는 간결한 한 줄 형식이 있습니다: value_if_true if condition else value_if_false. 로직이 진정으로 간단하고 문장처럼 읽힐 때만 사용하세요.
label = "pass" if score >= 50 else "fail"이것은 삼항식이며 문장처럼 읽힙니다. 로직이 진정으로 간단할 때 사용하세요. elif가 관련된 경우에는 전체 버전을 작성하세요.
while 루프
while 루프는 조건이 True인 동안 블록을 반복합니다. 루프가 몇 번 실행되어야 하는지 미리 알 수 없을 때 사용하세요. 예를 들어 유효한 입력을 기다리거나 작업이 성공할 때까지 재시도하는 경우입니다.
lives = 3
while lives > 0:
print(f"Lives remaining: {lives}")
lives -= 1
print("Game over")while은 루프가 몇 번 실행될지 미리 알 수 없을 때 가장 좋습니다. 알고 있거나 컬렉션을 순회할 때는 for가 더 깔끔합니다.
break와 continue
break는 남은 반복 횟수에 관계없이 루프를 즉시 종료합니다. continue는 현재 반복의 나머지를 건너뛰고 조건 확인으로 다시 돌아갑니다. 둘 다 자신이 속한 가장 안쪽 루프에만 영향을 미칩니다.
break는 루프를 즉시 종료합니다:
target = 5
num = 0
while True:
num += 1
if num == target:
print(f"Found {target}")
break # 루프 중단break가 있는 while True:는 종료 조건이 복잡하거나 루프 본문의 끝에서 발생해야 할 때 유효하고 일반적인 패턴입니다.
continue는 현재 반복의 나머지를 건너뛰고 조건 확인으로 돌아갑니다:
num = 0
while num < 10:
num += 1
if num % 2 == 0:
continue # 짝수 건너뛰기
print(num) # 홀수만 출력됨: 1, 3, 5, 7, 9for 루프
for 루프는 시퀀스의 항목을 한 번에 하나씩 순회합니다: 리스트, 문자열, 숫자 범위 등. for 뒤에 이름 지정한 변수는 각 항목을 차례로 받습니다. 카운터를 관리하거나 직접 길이를 확인하지 않아도 됩니다.
players = ["민준", "서연", "지호"]
for player in players:
print(f"Hello, {player}!")for 루프는 문자열(문자별로 순회)이나 다른 시퀀스 타입에서도 작동합니다.
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()는 리스트를 생성하지 않습니다. 숫자를 한 번에 하나씩 생성하므로 매우 큰 범위에서도 효율적입니다.
enumerate()
enumerate()는 루프 진행 중에 인덱스와 값을 모두 제공하므로 카운터를 별도로 추적할 필요가 없습니다. i, player 부분은 각 반복에서 자동으로 한 쌍의 값을 받습니다.
players = ["민준", "서연", "지호"]
for i, player in enumerate(players):
print(f"{i + 1}. {player}")
# 1. 민준
# 2. 서연
# 3. 지호i, player 문법은 언패킹이라고 부릅니다. 파이썬이 (index, value) 쌍을 자동으로 두 이름으로 나눕니다.
기본적으로 enumerate()는 0에서 시작합니다. 변경하려면 시작 값을 전달하세요:
for i, player in enumerate(players, start=1):
print(f"{i}. {player}") # 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는 가장 안쪽 루프에만 영향을 미칩니다.
Loop-else
파이썬 루프는 break에 걸리지 않고 루프가 완료된 경우에만 실행되는 else 절을 가질 수 있습니다. 자주 사용되지는 않지만, "리스트를 검색하고 아무것도 찾지 못하면 이것을 수행"이라고 쓰는 가장 깔끔한 방법입니다.
target = "수빈"
names = ["민준", "서연", "지호"]
for name in names:
if name == target:
print(f"Found {target}")
break
else:
print(f"{target} not in list") # break가 발동하지 않았기 때문에 실행됨break가 실행되면 else는 건너뜁니다. 루프가 시퀀스를 소진하면 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]은 한 줄 함수입니다. 플레이어 튜플을 받아 점수를 반환합니다. 람다 함수는 람다, 컴프리헨션, zip 장에서 다룹니다.
간단한 경우에는 sorted()를 사용하세요. 제자리에서 수정하려는 리스트의 경우 .sort()를 사용하세요.
실전에서
점수를 순회하면서 총합을 누적하고, 합격 등급을 세고, 요약을 출력합니다:
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: {average:.1f}")
print(f"Passing: {passing}/{len(raw_scores)}")
print(f"Top score: {sorted(raw_scores, reverse=True)[0]}")
