38.1 try except로 사용하기

Unit 38. 예외 처리 사용하기

예외(exception)란 코드를 실행하는 중에 발생한 에러를 뜻합니다. 다음과 같이 10을 어떤 값으로 나누는 함수 ten_div가 있을 때 인수에 따라 정상으로 동작하기도 하고 에러가 나기도 합니다.

>>> def ten_div(x):
...     return 10 / x
...

이 함수에 2를 넣으면 5.0이 나옵니다.

>>> ten_div(2)
5.0

하지만 0을 넣으면 실행하는 중에 에러가 발생합니다. 이런 상황을 예외라고 하는데 여기서는 어떤 숫자를 0으로 나누어서 ZeroDivisionError 예외가 발생했습니다.

>>> ten_div(0)
Traceback (most recent call last):
  File "<pyshell#121>", line 1, in <module>
    ten_div(0)
  File "<pyshell#119>", line 2, in ten_div
    return 10 / x
ZeroDivisionError: division by zero 

ZeroDivisionError뿐만 아니라 지금까지 만난 AttributeError, NameError, TypeError 등 다양한 에러들도 모두 예외입니다.

이번에는 예외가 발생했을 때도 스크립트 실행을 중단하지 않고 계속 실행하게 해주는 예외 처리 방법에 대해 알아보겠습니다.

38.1 try except로 사용하기

예외 처리를 하려면 다음과 같이 try에 실행할 코드를 넣고 except에 예외가 발생했을 때 처리하는 코드를 넣습니다.

try:
    실행할 코드
except:
    예외가 발생했을  처리하는 코드

이제 숫자를 0으로 나누었을 때 발생하는 예외를 처리해보겠습니다.

try_except.py

try:
    x = int(input('나눌 숫자를 입력하세요: '))
    y = 10 / x
    print(y)
except:    # 예외가 발생했을 때 실행됨
    print('예외가 발생했습니다.')

소스 코드를 실행한 뒤 0을 입력하고 엔터 키를 누르세요.

실행 결과

나눌 숫자를 입력하세요: 0 (입력)
예외가 발생했습니다.

숫자를 0으로 나누면 ZeroDivisionError 예외가 발생합니다. 여기서는 except에서 예외 처리를 하도록 만들었으므로 '예외가 발생했습니다.'가 출력됩니다.

특히 예외가 발생하면 해당 줄에서 코드 실행을 중단하고 바로 except로 가서 코드를 실행합니다. 즉, tryy = 10 / x를 비롯하여 그 아래줄에 있는 print(y)도 실행되지 않습니다.

그림 38-1 예외 발생과 except

다시 소스 코드를 실행한 뒤 2를 입력하고 엔터 키를 누르세요.

나눌 숫자를 입력하세요: 2 (입력)
5.0

2를 입력하니 예외가 발생하지 않고 계산 결과가 잘 출력됩니다. 이처럼 try의 코드가 에러 없이 잘 실행 되면 except의 코드는 실행되지 않고 그냥 넘어갑니다. 즉, try의 코드에서 에러가 발생했을 때만 except의 코드가 실행됩니다.

38.1.1  특정 예외만 처리하기

이번에는 except에 예외 이름을 지정해서 특정 예외가 발생했을 때만 처리 코드를 실행하도록 만들어보겠습니다.

try:
    실행할 코드
except 예외이름:
    예외가 발생했을  처리하는 코드

다음과 같이 정수 두 개를 입력받아서 하나는 리스트의 인덱스로 사용하고, 하나는 나누는 값으로 사용합니다. 그리고 except를 두 개 사용하고 각각 ZeroDivisionErrorIndexError를 지정합니다.

try_except_exception.py

y = [10, 20, 30]
 
try:
    index, x = map(int, input('인덱스와 나눌 숫자를 입력하세요: ').split())
    print(y[index] / x)
except ZeroDivisionError:    # 숫자를 0으로 나눠서 에러가 발생했을 때 실행됨
    print('숫자를 0으로 나눌 수 없습니다.')
except IndexError:           # 범위를 벗어난 인덱스에 접근하여 에러가 발생했을 때 실행됨
    print('잘못된 인덱스입니다.')

소스 코드를 실행한 뒤 2 0을 입력하고 엔터 키를 누르세요.

인덱스와 나눌 숫자를 입력하세요: 2 0 (입력)
숫자를 0으로 나눌  없습니다.

2 0을 입력하면 10 / 0이 되므로 숫자를 0으로 나누게 됩니다. 이때는 except ZeroDivisionError:의 처리 코드가 실행됩니다.

다시 소스 코드를 실행한 뒤 3 5를 입력하고 엔터 키를 누릅니다.

인덱스와 나눌 숫자를 입력하세요: 3 5 (입력)
잘못된 인덱스입니다.

y = [10, 20, 30]은 요소가 3개 들어있는 리스트입니다. 따라서 인덱스에 3을 지정하면 범위를 벗어나게 됩니다. 이때는 except IndexError:의 처리 코드가 실행됩니다.

38.1.2  예외의 에러 메시지 받아오기

특히 except에서 as 뒤에 변수를 지정하면 발생한 예외의 에러 메시지를 받아올 수 있습니다.

try:
    실행할 코드
except 예외 as 변수:
    예외가 발생했을  처리하는 코드

앞에서 만든 코드의 exceptas e를 넣습니다. 보통 예외(exception)의 e를 따서 변수 이름을 e로 짓습니다.

try_except_as.py

y = [10, 20, 30]
 
try:
    index, x = map(int, input('인덱스와 나눌 숫자를 입력하세요: ').split())
    print(y[index] / x)
except ZeroDivisionError as e:                    # as 뒤에 변수를 지정하면 에러를 받아옴
    print('숫자를 0으로 나눌 수 없습니다.', e)    # e에 저장된 에러 메시지 출력
except IndexError as e:
    print('잘못된 인덱스입니다.', e)

실행 결과

인덱스와 나눌 숫자를 입력하세요: 2 0 (입력)
숫자를 0으로 나눌 수 없습니다. division by zero

실행 결과

인덱스와 나눌 숫자를 입력하세요: 3 5 (입력)
잘못된 인덱스입니다. list index out of range

2 0, 3 5처럼 예외가 발생하는 숫자를 넣어보면 해당 예외에 해당하는 에러 메시지가 출력됩니다. 단, 예외가 여러 개 발생하더라도 먼저 발생한 예외의 처리 코드만 실행됩니다(또는, 예외 중에서 높은 계층의 예외부터 처리됩니다. 기반 클래스 > 파생 클래스 순).

참고로 모든 예외의 에러 메시지를 출력하고 싶다면 다음과 같이 exceptException을 지정하고 as 뒤에 변수를 넣으면 됩니다.

except Exception as e:    # 모든 예외의 에러 메시지를 출력할 때는 Exception을 사용
    print('예외가 발생했습니다.', e)

이처럼 예외 처리는 에러가 발생하더라도 스크립트의 실행을 중단시키지 않고 계속 실행하고자 할 때 사용합니다.

참고 | 예외 계층

예외도 클래스 상속으로 구현되며 다음과 같은 계층으로 이루어져 있습니다. 보통 파이썬에서 새로운 예외를 만들 때는 Exception을 상속받아서 구현합니다.

전체 계층도는 파이썬 공식 문서를 참조하세요.

Built-in Exceptions: https://docs.python.org/3/library/exceptions.html#exception-hierarchy

그림 38-2 파이썬 예외 계층도