40.3 예외 발생시키기

지금까지 숫자를 0으로 나눴을 때 에러, 리스트의 범위를 벗어난 인덱스에 접근했을 때 에러 등 파이썬에서 정해진 예외만 처리했습니다. 이번에는 우리가 직접 예외를 발생시켜 보겠습니다.

예외를 발생시킬 때는 raise에 Exception을 지정하고 에러 메시지를 넣습니다(에러 메시지는 생략 할 수 있음).

  • raise Exception(에러메시지)

그럼 3의 배수를 입력받은 뒤 숫자가 3의 배수가 아니면 예외를 발생시켜보겠습니다.

try_except_raise.py

try:
    x = int(input('3의 배수를 입력하세요: '))
    if x % 3 != 0:                                 # x가 3의 배수가 아니면
        raise Exception('3의 배수가 아닙니다.')    # 예외를 발생시킴
    print(x)
except Exception as e:                             # 예외가 발생했을 때 실행됨
    print('예외가 발생했습니다.', e)

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

실행 결과

3의 배수를 입력하세요: 5 (입력)
예외가 발생했습니다. 3의 배수가 아닙니다.

5는 3의 배수가 아니므로 raise Exception('3의 배수가 아닙니다.')로 예외를 발생시켰습니다. 여기서 Exception에 넣은 에러 메시지는 except Exception as e:의 e에 들어갑니다.

그리고 raise로 예외를 발생시키면 raise 아래에 있는 코드는 실행되지 않고 바로 except로 넘어갑니다. 따라서 try의 print(x)는 실행되지 않습니다.

이번에는 raise의 처리 과정을 알아보겠습니다. 다음은 함수 안에서 raise를 사용하지만 함수 안에는 try except가 없는 상태입니다

try_except_function_raise.py

def three_multiple():
    x = int(input('3의 배수를 입력하세요: '))
    if x % 3 != 0:                                 # x가 3의 배수가 아니면
        raise Exception('3의 배수가 아닙니다.')    # 예외를 발생시킴
    print(x)                                       # 현재 함수 안에는 except가 없으므로
                                                   # 예외를 상위 코드 블록으로 넘김
 
try:
    three_multiple()
except Exception as e:                             # 하위 코드 블록에서 예외가 발생해도 실행됨
    print('예외가 발생했습니다.', e)

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

실행 결과

3의 배수를 입력하세요: 5 (입력)
예외가 발생했습니다. 3의 배수가 아닙니다.

three_multiple 함수는 안에 try except가 없는 상태에서 raise로 예외를 발생시켰습니다. 이렇게 되면 함수 바깥에 있는 except에서 예외가 처리됩니다. 즉, 예외가 발생하더라도 현재 코드 블록에서 처리해줄 except가 없다면 except가 나올 때까지 계속 상위 코드 블록으로 올라갑니다.

만약 함수 바깥에도 처리해줄 except가 없다면 코드 실행은 중지되고 에러가 표시됩니다. 다음은 파이썬 셸에서 직접 three_multiple 함수를 호출했으므로 except가 없는 상태입니다.

>>> three_multiple()
3의 배수를 입력하세요: 5 (입력)
Traceback (most recent call last):
  File "<pyshell#5>", line 1, in <module>
    three_multiple()
  File "C:₩project₩try_except_function_raise.py", line 4, in three_multiple
    raise Exception('3의 배수가 아닙니다.')    # 예외를 발생시킴
Exception: 3의 배수가 아닙니다.

이번에는 함수 안에서 처리한 예외를 함수 바깥에서 다시 처리하는 방법을 알아보겠습니다. 다음과 같이 except 안에서 raise만 사용하면 현재 예외를 다시 상위 코드 블록으로 넘깁니다.

  • raise

try_except_raise_in_except.py

def three_multiple():
    try:
        x = int(input('3의 배수를 입력하세요: '))
        if x % 3 != 0:                                 # x가 3의 배수가 아니면
            raise Exception('3의 배수가 아닙니다.')    # 예외를 발생시킴
        print(x)
    except Exception as e:                             # 함수 안에서 예외를 처리함
        print('three_multiple 함수에서 예외가 발생했습니다.', e)
        raise    # raise만 사용하면 현재 예외를 다시 상위 코드 블록으로 넘김
 
try:
    three_multiple()
except Exception as e:                                 # 하위 코드 블록에서 예외가 발생해도 실행됨
    print('스크립트 파일에서 예외가 발생했습니다.', e)

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

실행 결과

3의 배수를 입력하세요: 5 (입력)
three_multiple 함수에서 예외가 발생했습니다. 3의 배수가 아닙니다.
스크립트 파일에서 예외가 발생했습니다. 3의 배수가 아닙니다.

three_multiple 함수 안에서 발생한 예외를 함수 안의 except에서 한 번 처리하고, raise를 사용하여 다시 상위 코드 블록으로 넘겼습니다. 그다음에 함수 바깥의 except에서 예외를 처리했습니다. 이런 방식으로 같은 예외를 계속 처리해줄 수 있습니다.

참고로 raise만 사용하면 같은 예외를 상위 코드 블록으로 넘기지만 raise에 Exception을 지정하고 에러 메시지를 넣을 수도 있습니다. 따라서 다른 예외도 발생시킬 수 있습니다.

        if x % 3 != 0:
            raise Exception('3의 배수가 아닙니다.')
        print(x)
    except Exception as e:
        print('three_multiple 함수에서 예외가 발생했습니다.', e)
        raise Exception('three_multiple 함수에서 예외가 발생했습니다.')
참고 | assert로 예외 발생시키기

예외를 발생시키는 방법 중에 assert를 사용하는 방법도 있습니다. assert는 지정된 조건식이 거짓일 때 AssertionError 예외를 발생시키며 조건식이 참이면 그냥 넘어갑니다. 보통 assert는 디버깅 용도로 사용합니다. 다음은 3의 배수가 아니면 예외 발생, 3의 배수이면 그냥 넘어갑니다.

  • assert 조건식, 에러메시지

assertion.py

x = int(input('3의 배수를 입력하세요: '))
assert x % 3 == 0, '3의 배수가 아닙니다.'    # 3의 배수가 아니면 예외 발생, 3의 배수이면 그냥 넘어감
print(x)

실행 결과

3의 배수를 입력하세요: 5 (입력)
Traceback (most recent call last):
  File "C:₩project₩assertion.py", line 2, in <module>
    assert x % 3 == 0, '3의 배수가 아닙니다.'
AssertionError: 3의 배수가 아닙니다.

assert는 디버깅 모드에서만 실행됩니다. 특히 파이썬은 기본적으로 디버깅 모드이며(__debug__의 값이 True) assert가 실행되지 않게 하려면 python에 -O 옵션을 붙여서 실행합니다.

python -O 스크립트파일.py
최근 수정: 2017년 11월 9일, 목요일, 오후 1:36