티스토리 뷰

Python

[Python] 예외 처리 사용하기

SeYuNi 2024. 2. 8. 23:56

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

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

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

 

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

 

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

 

try except로 사용하기

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

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

 

숫자를 0으로 나누었을 때 발생하는 예외를 처리해보면,

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

실행결과

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

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

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

예외 발생과 except

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

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

 

특정 예외만 처리하기

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

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

 

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 (입력)
숫자를 0으로 나눌 수 없습니다.

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

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

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

 

예외의 에러 메세지 받아오기

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

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

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

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

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

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

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

 

else와 finally 사용하기

else는 except 바로 다음에 와야 하며 except를 생략할 수 없다.

try:
    실행할 코드
except:
    예외가 발생했을 때 처리하는 코드
else:
    예외가 발생하지 않았을 때 실행할 코드

 

try:
    x = int(input('나눌 숫자를 입력하세요: '))
    y = 10 / x
except ZeroDivisionError:    # 숫자를 0으로 나눠서 에러가 발생했을 때 실행됨
    print('숫자를 0으로 나눌 수 없습니다.')
else:                        # try의 코드에서 예외가 발생하지 않았을 때 실행됨
    print(y)

실행결과

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

2를 입력했으므로 y = 10 / x에서 예외가 발생하지 않았다. 따라서 else의 코드가 실행되고 계산 결과가 출력된다.

 

물론 0을 입력해서 예외가 발생하면 except의 코드만 실행되고 else의 코드는 실행되지 않는다.

나눌 숫자를 입력하세요: 0 (입력)
숫자를 0으로 나눌 수 없습니다.

 

예외와는 상관없이 항상 코드 실행하기

finally는 except와 else를 생략할 수 있다.

try:
    실행할 코드
except:
    예외가 발생했을 때 처리하는 코드
else:
    예외가 발생하지 않았을 때 실행할 코드
finally:
    예외 발생 여부와 상관없이 항상 실행할 코드

 

다음은 try의 코드가 끝나면 항상 '코드 실행이 끝났습니다.'를 출력한다.

try:
    x = int(input('나눌 숫자를 입력하세요: '))
    y = 10 / x
except ZeroDivisionError:    # 숫자를 0으로 나눠서 에러가 발생했을 때 실행됨
    print('숫자를 0으로 나눌 수 없습니다.')
else:                        # try의 코드에서 예외가 발생하지 않았을 때 실행됨
    print(y)
finally:                     # 예외 발생 여부와 상관없이 항상 실행됨
    print('코드 실행이 끝났습니다.')

실행결과

나눌 숫자를 입력하세요: 2 (입력)
5.0
코드 실행이 끝났습니다.

2를 입력하여 예외가 발생하지 않았으므로 계산 결과가 출력되고, '코드 실행이 끝났습니다.'도 출력된다.

나눌 숫자를 입력하세요: 0 (입력)
숫자를 0으로 나눌 수 없습니다.
코드 실행이 끝났습니다.

숫자를 0으로 나눠서 예외가 발생했지만 finally는 항상 실행되므로 '코드 실행이 끝났습니다.'가 출력된다.

try, except, else, finally의 실행 과정을 그림으로 나타내면 다음과 같은 모양이 된다.

try, except, else, finally의 실행 과정

 

예외 발생시키기

예외를 발생시킬 때는 raise에 예외를 지정하고 에러 메시지를 넣는다. 에러 메시지는 생략할 수 있다.

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

실행결과

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

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

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

 

raise의 처리 과정

다음은 함수 안에서 raise를 사용하지만 함수 안에는 try except가 없는 상태이다.

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)

실행결과

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

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

만약 함수 바깥에도 처리해줄 except가 없다면 코드 실행은 중지되고 에러가 표시된다. 

 

현재 예외를 다시 발생시키기

except 안에서 raise를 사용하면 현재 예외를 다시 발생시킨다.(re-raise)

  • raise

다음은 three_multiple 코드 블록의 예외를 다시 발생시킨 뒤 상위 코드 블록에서 예외를 처리한다.

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)

실행결과

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

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

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

 

  • raise 예외('에러메시지')
        if x % 3 != 0:
            raise Exception('3의 배수가 아닙니다.')
        print(x)
    except Exception as e:
        print('three_multiple 함수에서 예외가 발생했습니다.', e)
        raise RuntimeError('three_multiple 함수에서 예외가 발생했습니다.')

 

 

예외 만들기

프로그래머가 직접 만든 예외를 사용자 정의 예외라고 한다.

내장된 예외와 사용자 처리 예외

Exception을 상속받아서 새로운 클래스를 만들면 예외를 만들 수 있다. 그리고 __init__ 메서드에서 기반 클래스의 __init__ 메서드를 호출하면서 에러 메시지를 넣어주면 된다.

class 예외이름(Exception):
    def __init__(self):
        super().__init__('에러메시지')

 

입력된 숫자가 3의 배수가 아닐 때 발생시킬 예외를 만들어보면,

class NotThreeMultipleError(Exception):    # Exception을 상속받아서 새로운 예외를 만듦
    def __init__(self):
        super().__init__('3의 배수가 아닙니다.')
 
def three_multiple():
    try:
        x = int(input('3의 배수를 입력하세요: '))
        if x % 3 != 0:                     # x가 3의 배수가 아니면
            raise NotThreeMultipleError    # NotThreeMultipleError 예외를 발생시킴
        print(x)
    except Exception as e:
        print('예외가 발생했습니다.', e)
 
three_multiple()

실행결과

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

먼저 Exception을 상속받아서 NotThreeMultipleError 예외를 만들었다. 그리고 __init__ 메서드 안에서 기반 클래스의 __init__ 메서드를 호출하면서 에러 메시지를 넣었다.

class NotThreeMultipleError(Exception):    # Exception을 상속받아서 새로운 예외를 만듦
    def __init__(self):
        super().__init__('3의 배수가 아닙니다.')

예외를 발생시킬 때는 raise NotThreeMultipleError와 같이 raise에 새로 만든 예외를 지정해주면 된다.

 

참고로 다음과 같이 Exception만 상속받고 pass를 넣어서 아무것도 구현하지 않아도 된다.

class NotThreeMultipleError(Exception):    # Exception만 상속받고
    pass                                   # 아무것도 구현하지 않음

이때는 예외를 발생시킬 때 에러 메시지를 넣어주면 된다.

raise NotThreeMultipleError('3의 배수가 아닙니다.')    # 예외를 발생시킬 때 에러 메시지를 넣음

 

 

 

 

 

 

 

 

 

 

 

'Python' 카테고리의 다른 글

[Python] 클래스 (3)  (1) 2024.02.08
[Python] 클래스 (2)  (0) 2024.02.08
[Python] 클래스 (1)  (0) 2024.02.08
[Python] 람다 표현식으로 함수 만들기  (0) 2024.02.08
[Python] 기본 문법 정리 (4)  (0) 2024.02.03
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
TAG
more
«   2024/07   »
1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30 31
글 보관함