오류가 날 수 있는 코드를 try 안에 두고 except 오류이름:을 아래에 쓰면, 그 오류가 나더라도 프로그램이 멈추지 않고 except 안의 코드를 대신 실행합니다.
입력은 늘 글자에서는 int(input('나이: '))로 사용자가 친 글자를 정수로 바꾸었습니다. 사용자가 '20'을 치면 잘 돌지만, 실수로 '스물'이나 '열'처럼 숫자가 아닌 글자를 치면 ValueError가 나면서 프로그램이 그 자리에서 멈춰 버립니다. 사용자에게 '숫자로 적어 주세요'라고 친절히 안내하고 프로그램을 이어 가려면, 오류가 났을 때 멈추는 대신 다른 길로 건너뛰는 장치가 필요합니다.
1try와 except의 흐름
오류가 날 수 있는 줄을 try: 아래에 들여 쓰고, 그 오류가 났을 때 대신 실행할 줄을 except 오류이름: 아래에 들여 씁니다.
text = '열'
try:
n = int(text)
print(n * 2)
except ValueError:
print('숫자로 적어 주세요')
숫자로 적어 주세요파이썬이 이 코드를 실행하는 순서는 다음과 같습니다.
try:안을 위에서부터 한 줄씩 실행합니다.- 3번 줄
n = int(text)에서int('열')이ValueError를 냅니다. - 오류가 나는 순간
try안의 남은 줄(4번 줄print(n * 2))은 건너뛰고, 방금 난 오류 이름과 맞는except ValueError:로 바로 넘어갑니다. - 6번 줄
print('숫자로 적어 주세요')를 실행하고 프로그램이 무사히 끝납니다.
반대로 try 안에서 오류가 하나도 나지 않으면 어떻게 될까요? text를 '10'으로 바꾼 아래 코드의 실행 결과를 먼저 맞혀 보세요.
text = '10'
try:
n = int(text)
print(n * 2)
except ValueError:
print('숫자로 적어 주세요')
print('끝')
실행 결과 보기
20
끝예상과 다르면 그곳이 배울 곳입니다. int('10')은 오류 없이 정수 10을 돌려주므로 4번 줄 print(n * 2)까지 마저 실행되어 20이 찍힙니다. 오류가 나지 않았으니 except ValueError: 덩어리는 통째로 건너뛰고, 바깥의 7번 줄 print('끝')으로 이어집니다.
| 상황 | try 안의 남은 줄 | except 덩어리 | 그다음 바깥 줄 |
|---|---|---|---|
try 안에서 오류 안 남 | 끝까지 실행함 | 건너뜀 | 이어서 실행함 |
try 안에서 지정한 오류 남 | 즉시 건너뜀 | 실행함 | 이어서 실행함 |
2자주 잡는 오류 이름과 필요한 줄만 감싸기
except 뒤에는 어떤 오류를 잡을지 이름을 밝혀 적습니다. 입문 단계에서 가장 자주 만나는 오류 이름은 두 가지입니다.
ValueError:int('열')이나float('세개')처럼 값의 모양이 맞지 않아 바꿀 수 없을 때 납니다.ZeroDivisionError:100 / 0이나100 // 0처럼0으로 나누려 할 때 납니다.
예를 들어 나눌 수 d가 0일 때 멈추지 않고 안내 문구를 찍으려면 except ZeroDivisionError:를 씁니다.
d = 0
try:
q = 100 // d
print('몫:', q)
except ZeroDivisionError:
print('0으로는 나눌 수 없습니다')
0으로는 나눌 수 없습니다또한 try: 안에는 실제로 그 오류가 날 수 있는 줄만 짧게 넣는 것이 좋습니다. 오류와 상관없는 앞뒤 계산까지 try 안에 잔뜩 넣어 두면, 어느 줄에서 오류가 나서 except로 넘어왔는지 파악하기 어렵고 엉뚱한 실수까지 가려지기 때문입니다.
raw = '25'
try:
age = int(raw)
except ValueError:
age = 0
print('내년 나이:', age + 1)
내년 나이: 26변환이 실패할 수 있는 age = int(raw) 한 줄만 try에 넣고, 변환이 끝난 뒤의 print('내년 나이:', age + 1)은 바깥에 두었습니다.
예제
입력받은 글자를 정수로 바꾸기
입력은 늘 글자에서 썼던 input()과 try / except ValueError:를 합치면, 사용자가 숫자가 아닌 글자를 넣어도 프로그램이 죽지 않습니다. '다섯'을 넣었을 때의 결과입니다.
raw = input('개수: ')
try:
count = int(raw)
print(count * 1000, '원')
except ValueError:
print('숫자로 적어 주세요')넣는 값다섯
개수: 다섯
숫자로 적어 주세요같은 코드에 '3'을 넣으면 try 안이 끝까지 실행되고 except는 건너뜁니다.
raw = input('개수: ')
try:
count = int(raw)
print(count * 1000, '원')
except ValueError:
print('숫자로 적어 주세요')넣는 값3
개수: 3
3000 원리스트 안에서 숫자로 바꿀 수 있는 값만 더하기
문자열 리스트 ['10', '없음', '20']에서 숫자로 바꿀 수 있는 값만 골라 합을 구할 때도 반복문 안에 try / except ValueError:를 둘 수 있습니다. 무엇이 찍힐지 먼저 맞혀 보세요.
items = ['10', '없음', '20']
total = 0
for s in items:
try:
total = total + int(s)
except ValueError:
print('건너뜀:', s)
print('합:', total)
실행 결과 보기
건너뜀: 없음
합: 30예상과 다르면 그곳이 배울 곳입니다. 두 번째 바퀴에서 int('없음')이 ValueError를 내면 덧셈이 일어나지 않고 바로 except ValueError:로 가서 '건너뜀: 없음'을 찍습니다. 반복문은 멈추지 않고 세 번째 바퀴('20')로 이어져 합 30을 구합니다.
자주 틀리는 곳
1. 오류가 난 줄에서도 대입이 끝났거나 아래 줄이 돈다고 생각한다
틀린 생각: n = int('열')에서 오류가 나도 n에는 어떤 값이 들어가고, try 안의 다음 줄도 실행된다.
왜 이렇게 생각하기 쉬운가 하면, 위에서 아래로 모든 줄을 차례대로 실행한다는 규칙만 떠올리기 때문입니다. 그러나 대입문 오른쪽(int('열'))을 계산하다가 오류가 나면 대입 자체가 일어나지 않아 왼쪽 이름 n이 생기지 않으며(규칙 3), try 안의 남은 줄도 즉시 건너뜁니다.
text = '열'
try:
print('변환 전')
n = int(text)
print('변환 후:', n)
except ValueError:
print('변환 실패')
변환 전
변환 실패3번 줄 '변환 전'은 찍혔지만, 4번 줄 int(text)에서 ValueError가 나는 순간 5번 줄 print('변환 후:', n)은 건너뛰고 7번 줄로 넘어갔습니다. 이때 n은 아예 만들어지지 않았으므로 except 안에서 print(n)을 쓰면 NameError가 납니다.
맞는 규칙: try 안에서 오류가 나는 순간 그 줄의 대입은 취소되고, try 안의 남은 줄은 모두 건너뛰어 바로 except로 이동합니다.
2. 오류 이름 없이 except:만 써서 내 오타까지 숨긴다
틀린 생각: except: 뒤에 오류 이름을 적지 않으면 모든 오류를 다 잡아 주니 더 편하고 안전하다.
왜 이렇게 생각하기 쉬운가 하면, 어떤 오류가 나든 프로그램이 멈추지 않는 것이 좋아 보이기 때문입니다. 그러나 오류 이름을 적지 않은 맨몸 except:는 사용자가 잘못 입력해서 난 ValueError뿐 아니라, 내가 변수 이름을 잘못 적은 NameError까지 전부 삼켜 버립니다.
text = '10'
try:
n = int(text)
print(m * 2)
except:
print('숫자로 적어 주세요')
숫자로 적어 주세요text는 '10'이라 int(text)는 성공했습니다. 그런데 4번 줄에서 n 대신 없는 이름 m을 잘못 적어 NameError가 났는데도, 맨몸 except:가 이를 삼키고 '숫자로 적어 주세요'라고 거짓 안내를 내보냅니다. 내 코드의 오타인데 입력 탓으로 보여 버그를 찾기가 매우 어려워집니다.
맞는 규칙: except ValueError:처럼 내가 예상하고 처리하려는 오류 이름을 반드시 밝혀 적습니다.
3. 다른 종류의 오류도 같은 except가 잡아 줄 것이라 생각한다
틀린 생각: except ValueError:를 적어 두었으니 try 안에서 0으로 나누는 오류가 나도 잡아 준다.
왜 이렇게 생각하기 쉬운가 하면, try로 감싸기만 하면 그 안의 모든 오류가 해결된다고 느끼기 때문입니다. 그러나 except ValueError:는 오직 ValueError만 잡으며, 이름이 다른 오류는 잡지 못하고 프로그램이 그대로 멈춥니다.
d = 0
try:
print(100 // d)
except ValueError:
print('값 오류')
Traceback (most recent call last): File "main.py", line 3, in <module> print(100 // d) ~~~~^^~~ZeroDivisionError: division by zero3번 줄에서 난 오류는 ZeroDivisionError인데, 4번 줄은 ValueError만 기다리고 있으므로 잡히지 않고 멈추었습니다.
맞는 규칙: except 오류이름:은 지정한 이름과 일치하는 오류만 잡습니다. 0으로 나누는 오류를 잡으려면 except ZeroDivisionError:를 써야 합니다.
확인 문제
1. 다음 코드를 실행하면 무엇이 출력될까요?
text = '5'
try:
n = int(text)
print(n + 10)
except ValueError:
print('오류')
print('완료')
힌트
int('5')는 오류 없이 정수 5가 됩니다. try 안에서 오류가 나지 않았을 때 except 블록과 바깥의 마지막 줄이 각각 어떻게 되는지 생각해 보세요.
정답
15
완료int('5')가 성공하므로 n + 10인 15가 찍히고, except ValueError: 블록은 건너뛴 뒤 바깥의 print('완료')가 실행됩니다.
2. 문자열 raw = '백'을 정수로 바꾸려다 변환할 수 없는 값이면 '숫자가 아닙니다'를 출력하도록 빈칸을 채워 보세요.
raw = '백'
try:
price = int(raw)
print(price)
______ __________:
print('숫자가 아닙니다')
힌트
오류를 받아 내는 키워드와, int('백')처럼 값의 모양이 맞지 않을 때 나는 오류 이름을 나란히 적습니다.
정답
except ValueError:를 넣으면 됩니다. int('백')에서 나는 ValueError를 받아 '숫자가 아닙니다'를 출력합니다.
3. 다음 코드를 실행하면 무엇이 출력될까요?
items = ['4', '둘', '6']
for s in items:
try:
n = int(s)
print(n * 10)
except ValueError:
print('패스')
힌트
바퀴마다 s가 '4', '둘', '6'으로 바뀝니다. '둘'에서 ValueError가 날 때 5번 줄 print(n * 10)이 실행되는지, 건너뛰어지는지 확인해 보세요.
정답
40
패스
60첫 바퀴('4')에서는 40이 찍히고, 두 번째 바퀴('둘')에서는 int('둘')이 ValueError를 내어 print(n * 10)을 건너뛰고 '패스'가 찍힙니다. 세 번째 바퀴('6')에서는 다시 60이 찍힙니다.
아래 코드에서 text의 값을 '열'에서 '25'로도 바꾸어 돌려 보고, 왜 '열'일 때는 print('두 배:', n * 2)가 실행되지 않는지 확인해 보세요.
처음 펼 때 약 6MB를 받습니다.컴퓨터에서 해 보기
파이썬 3.14. Tab을 누르면 다음 단추나 칸으로 갑니다. 들여쓰기는 빈칸 네 개입니다. Enter는 앞줄 들여쓰기를 잇고, 콜론 뒤에서는 한 단계 더 들여 씁니다. Ctrl+Enter로 실행합니다.
'실행'을 누르면 여기에 나옵니다.
여기서 쓴 코드 · 내 예상은 저장하지도 운영자에게 보내지도 않습니다. 페이지를 새로 열면 처음 코드입니다.