Skip to content

Latest commit

 

History

History

step03_controlstmt

Folders and files

NameName
Last commit message
Last commit date

parent directory

..
 
 
 
 
 
 
 
 
 
 
 
 

README.md

step03_controlstmt — 조건문 · switch 흉내 · 난수 · 명령행 인자

학습 순서 3번. 프로그램이 처음으로 판단을 하기 시작합니다. 이 저장소의 마지막 챕터입니다.

학습 목표

  • if / if~else / if~elif~else / 중첩 if 를 단계적으로 익힌다
  • 3항 연산자로 한 줄 조건문을 쓴다
  • 파이썬에 switch 가 없다는 것과, dict 로 흉내 내는 방법을 안다
  • random 모듈로 난수를 만든다
  • sys.argv 로 명령행 인자를 받는다

파일 지도

randomEx.py               난수 7종
ifex/
  ifEx0_scratch.py        저장소 맨 위에 있던 13줄 습작 (아래 참고)
  ifEx1.py         ★     if 문 4단계 발전 과정이 주석으로 층층이 쌓여 있음
  ifEx2.py                중첩 if
  ifEx3.py                연습문제 — 크기·홀짝·부호 판정  ⚠️ 결함 있음
  ifEx4.py                연습문제 — 3과목 합격 판정      ⚠️ 결함 있음
switchex/
  switchEx.py             dict.get() 으로 switch 흉내
inputex/
  argvEx.py               sys.argv
  inputTest3.py           input() 복습
  inputTest4.py           연습문제 — 원의 둘레·넓이

ifEx0_scratch.py 는 원래 저장소 루트에 ifEX1.py 라는 이름으로 있었습니다. 같은 폴더의 ifEx1.py 와 대소문자만 다르고 내용은 전혀 달라 헷갈리기 딱 좋았습니다. 내용은 그대로 두고 이름과 위치만 정리했습니다. 문자열 비교와 %10s / {0:<10} 자릿수 맞추기를 처음 시험해 본 흔적입니다.

실행 방법

python3 step03_controlstmt/randomEx.py            # 입력 불필요
python3 step03_controlstmt/ifex/ifEx1.py          # 입력 불필요
python3 step03_controlstmt/switchex/switchEx.py   # 입력 불필요

python3 step03_controlstmt/ifex/ifEx4.py          # ← 점수 3개 입력
python3 step03_controlstmt/inputex/inputTest4.py  # ← 반지름 입력

python3 step03_controlstmt/inputex/argvEx.py 10 20 30   # ← 인자 3개 필수

argvEx.py 를 인자 없이 실행하면 IndexError 가 납니다. 버그가 아니라 인자를 요구하는 프로그램입니다.

짚고 갈 지점

ifEx1.py 는 아래에서 위로 읽으세요

이 파일은 특이합니다. 수업이 진행된 순서가 주석으로 층층이 쌓여 있고, 파일 끝의 번호(1, 2, 3, 4)가 그 순서입니다.

파일 맨 아래 ─ 1. if 문       (가장 단순)
             ─ 2. if~else
             ─ 3. if~elif~else
파일 맨 위   ─ 4. 단순 if 나열 + 3항 연산자   (활성 상태)

아래에서 위로 읽으면 조건문이 어떻게 발전하는지 보입니다.

특히 3번과 4번을 비교해 보세요. 같은 학점 판정을 두 방식으로 짰습니다.

# 4번 (현재 활성) — if 를 5개 나열. 조건이 겹치지 않게 직접 관리해야 한다
if 90 <= score <= 100: grade = 'A'
if score >= 80 and score < 90: grade = 'B'
...

# 3번 (주석) — elif 는 앞 조건이 거짓일 때만 검사한다
if score >= 90: grade = 'A'
elif score >= 80: grade = 'B'
...

elif 쪽이 짧고 안전합니다. if 를 나열하면 조건 하나만 잘못 써도 여러 개가 동시에 참이 되어 마지막 것이 이깁니다.

4번의 90 <= score <= 100 은 파이썬 고유 문법입니다. 대부분의 언어에서는 score >= 90 and score <= 100 이라고 써야 합니다.

3항 연산자

print('합격') if score >= 60 else print('불합격')

값_참 if 조건 else 값_거짓 순서입니다. 조건이 가운데 온다는 점이 다른 언어(조건 ? 참 : 거짓)와 달라 처음엔 어색합니다.

파이썬에는 switch 가 없다

def switch(x):
    return {
        'one': 1,
        'two': 2,
        'three': 3
    }.get(x, 9)      # 없는 키면 9 (default 역할)

dict 를 만들고 .get(키, 기본값) 으로 꺼내는 방식입니다. .get() 은 키가 없어도 에러 대신 기본값을 돌려주기 때문에 이게 가능합니다. (dict[키] 로 꺼내면 KeyError 가 납니다.)

이 방식에는 함정이 있습니다. 값 자리에 함수 호출을 넣으면 매번 전부 실행됩니다. test/class_ex_01.py 가 정확히 이 함정에 빠져 있으니 test/README.md 를 보세요.

난수 7종

randomEx.py 는 random 모듈의 주요 함수를 한 번에 보여 줍니다.

함수 범위
random() 0.0 이상 1.0 미만 실수
uniform(1.0, 3.14) 1.0 이상 3.14 미만 실수
randint(1, 100) 1 이상 100 이하 정수 — 끝값 포함
randrange(1, 100, 3) 1부터 100 미만, 3씩 건너뛰며
choice([...]) 목록에서 하나
sample(range(1,46), 6) 범위에서 6개, 중복 없이 (로또!)

randint 만 끝값을 포함한다는 점을 꼭 기억하세요. 나머지는 전부 "미만"입니다.

from random import *      # 모듈의 모든 함수를 이름 그대로 가져온다

from X import * 는 편하지만 어떤 이름이 딸려 왔는지 알 수 없어 실무에서는 피합니다. import random 후 random.randint(...) 로 쓰거나 필요한 것만 from random import randint 하는 편이 낫습니다.

명령행 인자

import sys
x = sys.argv[0]        # 프로그램 파일 이름 자신
a = int(sys.argv[1])   # 첫 번째 인자

sys.argv[0] 이 인자가 아니라 파일 이름이라는 게 핵심입니다. 실제 인자는 [1] 부터입니다. 그리고 input() 과 마찬가지로 전부 문자열이라 int() 변환이 필요합니다.

알려진 결함

고치지 않고 남겨 두었습니다. 찾아서 고치는 것이 연습 과제입니다.

ifEx3.py — 도달할 수 없는 else

if a > 0:
    print(a, '는 양수입니다')
elif a <= 0:
    print(a, '는 음수입니다')
else:
    print(a, '는 zero입니다')     # ← 여기는 절대 실행되지 않는다
왜 그럴까

a 가 0 이면 a > 0 은 거짓이고 a <= 0 은 참입니다. 그래서 "음수입니다"가 출력되고 else 로는 넘어가지 않습니다. 모든 숫자는 a > 0 이거나 a <= 0 둘 중 하나라서 else 에 도달할 방법이 없습니다.

문제 지문은 a>0 a<0 a==0 세 갈래를 요구했습니다. 두 번째를 elif a < 0 으로 고쳐야 합니다.

바로 위쪽에도 비슷한 문제가 있습니다.

if a >= 50: print(a, '는 50보다 크다')
if a <= 50: print(a, '는 50보다 작다')

a 가 정확히 50 이면 둘 다 출력됩니다. "50보다 크다"와 "50보다 작다"가 동시에 나오죠.

ifEx4.py — 같은 판정, 다른 결과

같은 합격 판정을 두 가지 방식으로 짜 놓았습니다. 그런데 평균이 정확히 60일 때 결과가 엇갈립니다.

# 방식 1
if avg >= 60:            # 60 → 이쪽 (합격 계열)
    ...
else:
    print('불합격')

# 방식 2
if avg <= 60:            # 60 → 이쪽 (불합격)
    print('불합격')
else:
    ...

avg 가 60 일 때 방식 1은 합격/과락 판정으로 가고, 방식 2는 불합격이라고 합니다. 실행해서 직접 확인해 보세요 — 국어 60, 영어 60, 수학 60을 넣으면 됩니다.

어느 쪽이 맞을까

문제 지문은 "평균이 60점이상인 경우 합격"이라고 했습니다. 그러니 avg >= 60 이 맞고, 방식 2의 avg <= 60 은 avg < 60 이어야 합니다.

경계값(>= 인가 > 인가)은 조건문에서 가장 흔한 버그 원인입니다. 조건을 쓸 때마다 "딱 그 값일 때는 어디로 가는가"를 확인하는 습관을 들이세요.

지금은 이렇게 바뀌었습니다

Python 3.10 부터 match 문이 생겼습니다. dict 로 흉내 낼 필요가 없어졌습니다.

# 이 챕터 (dict 흉내)
def switch(x):
    return {'one': 1, 'two': 2, 'three': 3}.get(x, 9)

# 지금 (match)
def switch(x):
    match x:
        case 'one':   return 1
        case 'two':   return 2
        case 'three': return 3
        case _:       return 9      # _ 가 default

match 는 값 비교뿐 아니라 자료구조 분해까지 되어 dict.get() 보다 훨씬 강력합니다. 다만 dict 방식도 여전히 유효합니다 — 특히 분기가 단순한 값 대응일 때는 더 짧습니다.

그 밖에:

  • from random import * 대신 import random 을 씁니다.
  • 명령행 인자가 많아지면 sys.argv 대신 표준 라이브러리 argparse 를 씁니다. 도움말과 타입 검사를 알아서 해 줍니다.
  • 출력은 f-string이 편합니다 — print(f'{a} 는 양수입니다').

연습 과제

  1. ifEx3.py 의 도달 불가 else 를 고치고, a = 0 으로 바꿔 "zero입니다"가 나오는지 확인하세요.
  2. ifEx3.py 위쪽의 >= 50 / <= 50 도 a = 50 일 때 하나만 나오도록 고치세요.
  3. ifEx4.py 의 두 방식이 평균 60에서 같은 답을 내도록 고치세요. 세 과목에 60을 넣어 확인하세요.
  4. ifEx1.py 의 활성 코드(단순 if 5개 나열)를 if~elif~else 로 바꾸세요. 몇 줄이 줄어드나요?
  5. switchEx.py 를 match 문으로 다시 쓰세요.
  6. randomEx.py 의 sample(range(1,46), 6) 을 이용해 로또 번호를 정렬해서 출력하세요.
  7. argvEx.py 를 인자 개수와 무관하게 동작하도록 고치세요. 인자가 없으면 안내 메시지를 내고, 있으면 전부 더하도록.