step체계에 편입되지 않은 실습 파일들입니다. 원본 수업 기록이라 고치지 않고 그대로 두었습니다.클래스와 람다는 이제 정식 챕터에서 배웁니다 —
step07_class와step06_function. 아래class_ex_01.py의 버그를 고친 정본은step07_class/calculator.py에 있습니다.
| 파일 | 성격 |
|---|---|
class_ex_01.py |
★ 클래스 + 예외 처리 + 람다 — 이 저장소에서 가장 복잡한 코드. |
lambda1.py |
람다(익명 함수) 최소 예제 |
randomEx.py |
step03_controlstmt/randomEx.py 의 초기판. 정본은 step03 쪽 |
argeEx.py |
step03_controlstmt/inputex/argvEx.py 의 초기판. 정본은 step03 쪽 (파일명 arge 는 argv 오타) |
testfile.py |
저장소 최초 업로드 테스트용 5줄 |
중복 파일 2개는 지우지 않고 남겼습니다.
step03정본과 나란히 놓고 보면 수업이 진행되면서 코드가 어떻게 다듬어졌는지 보입니다. 예를 들어randomEx.py는 초기판에 없던 주석과choice/sample설명이 정본에 붙어 있습니다.내용이 아예 없던
switchEx.py(0바이트)만 제거했습니다. 정본은step03_controlstmt/switchex/switchEx.py입니다.
python3 test/lambda1.py # 입력 불필요
python3 test/randomEx.py # 입력 불필요
python3 test/testfile.py # 입력 불필요
python3 test/class_ex_01.py # ← 무한 루프. 종료는 Ctrl+C
python3 test/argeEx.py 1 2 3 # ← 인자 3개 필수class Calculator:
def __init__(self): # 생성자 — 객체가 만들어질 때 자동 호출
self.result = 0
self.x = 0
self.y = 0
def setData(self, num1, num2): # 메서드는 첫 인자로 self 를 받는다
self.x = num1
self.y = num2
def add(self):
self.result = self.x + self.y
return self.result
cal1 = Calculator() # 객체 생성 → __init__ 이 실행된다
cal1.setData(3, 4) # self 는 자동으로 넘어간다 (인자 2개만 쓴다)
print(cal1.add()) # 7self는 그 객체 자신을 가리킵니다. 메서드 정의에는 반드시 쓰지만, 호출할 때는 넘기지 않습니다.self.x처럼self.를 붙인 값은 객체가 계속 들고 있습니다. 그냥x = num1이라고 쓰면 메서드가 끝나는 순간 사라집니다.
add = lambda a, b: a + b # def add(a, b): return a+b 와 같다
print(add(3, 4)) # 7lambda1.py 가 최소 예제입니다. 짧은 함수를 변수에 담아 쓸 수 있습니다.
이 파일에는 찾기 어려운 진짜 버그가 있습니다. 실행해서 재현해 보세요.
연산 종류 (+,-,*,/) : +
첫 번째 입력값 : 10
두 번째 입력값 : 0
숫자를 입력하세요. ← ??? 숫자를 넣었는데?
덧셈을 요청했고 숫자만 입력했는데 "숫자를 입력하세요"가 나옵니다. 원인은 이 한 줄입니다.
test = lambda x: {"+": cal1.add(), "-": cal1.sub(), "*": cal1.mul(), "/": cal1.div()}.get(x, "연산 종류가 틀리다")무슨 일이 벌어지나
dict 를 만드는 순간 값 자리의 함수가 전부 호출됩니다.
{"+": cal1.add(), ...} 에서 cal1.add() 는 "덧셈 함수"가 아니라 "덧셈을 실행한 결과값" 입니다. 괄호 () 가 붙어 있으니까요. 그래서 dict 가 만들어질 때 add·sub·mul·div 가 네 개 다 실행됩니다.
.get(x) 는 이미 계산이 끝난 네 값 중 하나를 고를 뿐입니다. 고르기 전에 이미 다 돌아간 뒤입니다.
두 번째 값이 0 이면 div() 가 ZeroDivisionError 를 냅니다. 덧셈을 요청했어도 마찬가지입니다. 그리고 그 예외를 아래의
except Exception as e:
print("숫자를 입력하세요.")가 종류를 가리지 않고 전부 잡아서 엉뚱한 메시지를 냅니다. 진짜 원인(ZeroDivisionError)은 e 에 담겨 있는데 출력하지도 않습니다.
고치는 방향은 두 가지입니다.
- 괄호를 떼서 함수 자체를 담고, 고른 다음에 호출합니다.
ops = {"+": cal1.add, "-": cal1.sub, "*": cal1.mul, "/": cal1.div} # () 없음 func = ops.get(three) print("결과 : ", func() if func else "연산 종류가 틀리다")
- 예외를 종류별로 나눠 잡습니다.
except ValueError: print("숫자를 입력하세요.") except ZeroDivisionError: print("0으로 나눌 수 없습니다.")
except Exception 으로 전부 삼키면 버그가 사용자에게 거짓말로 도착합니다. 이 파일이 그 실물 사례입니다.
두 가지를 모두 적용한 완성본이 step07_class/calculator.py 입니다. 먼저 직접 고쳐 본 뒤에 비교해 보세요.
이 함정은
step03_controlstmt/switchex/switchEx.py의dictswitch 패턴을 응용하다 생겼습니다. 원본은 값이1,2,3같은 상수라 문제가 없었지만, 값 자리에 함수 호출을 넣는 순간 성질이 달라집니다.
그 밖에 이 파일에는:
while True:에 종료 조건이 없어Ctrl+C로만 빠져나올 수 있습니다.except가 잡은e를 쓰지 않습니다.print(e)한 줄만 있어도 원인이 바로 보였을 겁니다.
-
단순히 값을 담는 클래스라면
@dataclass로 짧게 쓸 수 있습니다.from dataclasses import dataclass @dataclass class Calculator: x: int = 0 y: int = 0 result: int = 0
-
연산자 대응은
operator모듈을 쓰면 직접 만들 필요가 없습니다.import operator ops = {"+": operator.add, "-": operator.sub, "*": operator.mul, "/": operator.truediv} print(ops[three](one, two))
값 자리에 함수 자체(괄호 없음)가 들어간다는 점을 위 버그와 비교해 보세요.
-
except Exception은 지금도 그때도 나쁜 습관입니다. 잡을 예외를 구체적으로 적고, 잡았다면 최소한 로그를 남깁니다.
class_ex_01.py를 실행해+/10/0을 입력하고 잘못된 메시지를 직접 재현하세요.except Exception as e:아래에print(e)를 추가해 진짜 원인을 확인하세요.- 위 알려진 결함의 1번 방향대로
dict에서 괄호를 떼어 고치세요. - 예외를
ValueError와ZeroDivisionError로 나눠 각각 다른 메시지를 내도록 고치세요. q를 입력하면 종료되도록while True에 탈출 조건을 넣으세요.Calculator에 나머지(%)와 거듭제곱(**) 연산을 추가하세요.test/randomEx.py와step03_controlstmt/randomEx.py를 나란히 열어 무엇이 추가됐는지 찾아보세요.