Python에서 try, except, raise로 예외 처리 하기.
Python에서의 예외 처리 방법 2020-04-21
주의! Caution!
해당 게시글은 Archive된 게시글 입니다.
Archive된 사유는 다음 중 하나에 해당 됩니다.
  • 작성 된지 너무 오랜 시간이 경과 하여, API가 변경 되었을 가능성이 높은 경우
  • 주인장의 Data Engineering으로의 변경으로 질문의 답변이 어려워진 경우
  • 글의 퀄리티가 좋지 않아 글을 다시 작성 할 필요가 있을 경우
이 점 유의하여 게시글을 참고 하시길 바랍니다.

항상 프로그램은 사용자의 실수든, 코드를 잘못 작성 했든, 원하지 않는 방향으로 흘러 갈 때가 있습니다. 그걸 방지 하기 위해선, 예외처리 라는 것이 필요 합니다. 예외처리는 프로그래밍의 세계에선 상당히 중요합니다. 프로그램의 비정상적인 종료를 방지하고, 우리의 프로그램 설계 상 무엇이 잘못되었는지 알 수 있게끔 해주는 척도이기도 합니다. Python도 다른 언어와 마찬가지로, try, except 키워드를 이용하여, 예외 처리를 실시 할 수 있습니다. 한 번 가볍게 알아 볼까요?

try, except의 기본 사용법

Python 에서 사용하는 try, except도 다른 언어에서 사용하듯 사용 하면 됩니다. try 문에서 예외처리 하고 싶은 코드를 작성 하고, except 문에서 try 문에서 작성한 코드가 에러를 발생 시켰을 때, 에러를 어떻게 처리 할 껀지 코드를 작성 하면 됩니다.

while True:
    try:  # 코드 작성
        x = int(input("숫자를 입력 하세요: "))
        break
    except ValueError:  # ValueError에 대한 특수한 예외 처리
        print("이건 숫자가 아닌데... 다시 입력 하세요!")

일단 여러 가지 에러 객체들에 대해서 알아 보겠습니다. 일단... 엄청 많으니 주로 사용하는 에러 객체들을 위주로 설명 하겠습니다.

Error List

  • AssertionError: assert 문이 제대로 작동하지 않을 때 발생합니다.
  • IndexError: 참조 하려는 인덱스가 범위를 벗어날 때 발생합니다.
  • KeyError: 참조 하려는 키가 기존 키 집합에서 찾을 수 없을 때 발생합니다.
  • KeyboardInterrupt: 사용자가 인터럽트 키(Control + C, 혹은 Delete)를 누를 때 발생하며, 모든 Exception을 잡는 코드에 의해 인터프리터가 종료하는 것을 막지 못하도록 Exception 상위에 있는 BaseException을 직접 계승 합니다.
  • MemoryError: 메모리가 부족하지만, 가비지 컬렉터가 일부 객체의 삭제를 함으로써 복구될 수 있는 경우 발생합니다.
  • NameError: 참조하는 지역, 전역 변수 혹은 함수, 클래스 등을 찾을 수 없을 때 발생합니다.
  • OSError: 시스템 함수가 시스템 관련 에러를 돌려줄 때 발생합니다. (파일을 찾을 수 없거나, 디스크가 찼거나..)
  • OverflowError: 산술 연산의 결과가 너무 커서 표현 할 수 없을 때, 혹은 정수 범위를 벗어났을 때 발생합니다.
  • RecursionError: 최대 재귀 깊이가 초과하였을 때 발생합니다.
  • TypeError: 연산이나 함수가 부적절한 데이터 타입의 객체에 적용 되었을 때 발생합니다.
  • ValueError: 연산이나 함수가 부적절한 값을 가진 객체에 적용 되었을 때 발생합니다.
  • ZeroDivisionError: 나누기, 나머지 연산의 두 번째 인자가 0일 때 발생합니다.

How To Handling Errors?

특정 에러만 핸들링하는 방법은, 에러 객체의 이름을 except 절 옆에 작성함으로써 가능합니다. 튜플 형태 또한 작성할 수 있습니다. except 절 옆에 아무것도 없다면, 나머지 모든 에러를 제너릭하게 핸들링 합니다. 이 코드를 이용할 때는 주의하여야 합니다. 그 나머지 프로그래밍 에러를 가릴 수도 있기 때문입니다.

while True:
    try:  # 코드 작성
        x = int(input("A를 입력 하세요: "))
        y = int(input("A를 몇으로 나눌 지 입력하세요: "))
        print(x // y)
        break
    except ValueError:
        print("이건 숫자나 정수가 아닌데... 다시 입력 하세요!")
    except (ZeroDivisionError, OverflowError):
        print("0으로 나누었거나, 값이 너무 큽니다. 다시 입력하세요!")
    except:
        print("아 이거 뭔데 이거")

많은 에러들은 Exception 클래스의 파생 클래스입니다. Exception 클래스를 받는다고 하고, 제너릭하게 에러를 처리 할 수 있습니다.

while True:
    try:  # 코드 작성
        x = int(input("A를 입력 하세요: "))
        y = int(input("A를 몇으로 나눌 지 입력하세요: "))
        print(x // y)
        break
    except ValueError:
        print("이건 숫자나 정수가 아닌데... 다시 입력 하세요!")
    except (ZeroDivisionError, OverflowError):
        print("0으로 나누었거나, 값이 너무 큽니다. 다시 입력하세요!")
    except Exception as e:
        print("아 이거 뭔데 이거 : ", e)

try ... except 문은 else 절을 가질 수도 있습니다. try 문이 에러 발생 없이 정상적으로 실행 되었을 때, else 문을 실행 할 수 있습니다.

while True:
    try:  # 코드 작성
        x = int(input("A를 입력 하세요: "))
        y = int(input("A를 몇으로 나눌 지 입력하세요: "))
    except ValueError:
        print("이건 숫자나 정수가 아닌데... 다시 입력 하세요!")
    except (ZeroDivisionError, OverflowError):
        print("0으로 나누었거나, 값이 너무 큽니다. 다시 입력하세요!")
    except Exception as e:
        print("아 이거 뭔데 이거", e)
    else:
        print(x // y)
        break

try ... except 문은 finally 절을 가질 수도 있습니다. 여기서 except 문이 실행 됐든, 안 됐든 상관없이, finally문을 실행 합니다.

    try:  # 코드 작성
        x = int(input("A를 입력 하세요: "))
        y = int(input("A를 몇으로 나눌 지 입력하세요: "))
    except ValueError:
        print("이건 숫자나 정수가 아닌데... 다시 입력 하세요!")
    except (ZeroDivisionError, OverflowError):
        print("0으로 나누었거나, 값이 너무 큽니다. 다시 입력하세요!")
    except Exception as e:
        print("아 이거 뭔데 이거", e)
    else:
        print(x // y)
        break
    finally:
        print("에러 처리 끝!")

What is raise? (예외 일으키기)

프로그래머가 인위적으로 에러를 발생 시킬 수 있습니다. 바로, raise 문을 통해서 말입니다. 만약 여러분들이 파싱을 해주는 함수를 만들었는데, 이상한 값이 들어 왔다고 알려 주기 위해 에러를 발생 시킬 수도 있습니다. 그렇다면 상위에 존재하는 try ... except 문에서 에러를 처리 할 수 있겠죠?

  • In
def str_to_fraction(string):
    arr = string.split('/')
    if len(arr) != 2:
        raise ValueError('분수를 입력 해 주세요')
    else:
        try:
            numerator = int(arr[0])
            denominator = int(arr[1])
            return {
                'numerator': numerator,
                'denominator': denominator,
                'value': numerator / denominator,
                'string': string
            }
        except ValueError:
            raise ValueError('정수를 입력 하세요')


try:
    str_to_fraction('1//2')
except ValueError as v:
    print(v)

try:
    str_to_fraction('1.1/2')
except ValueError as v:
    print(v)

try:
    str_to_fraction('1/2')
except ValueError as v:
    print(v)
  • Out
분수를 입력 해 주세요
정수를 입력 하세요

주의 사항

Exception 내의 메세지를 확인 하고 싶다면, except 에러 as e: 절 작성 후, 해당 절안에 e를 출력 해 주면 됩니다.

try:
    str_to_fraction('1/2')
except ValueError as v:
    print(v)

Make Error Class

Exception 객체를 직접 상속 받아, Error Class를 만들 수 있습니다. 간단하게 클래스 생성 시, Exception 클래스를 상속 받으면 됩니다.

  • In
class FloatError(Exception):
    def __init__(self, message='소수 말고 정수를 쓰세요'):
        self.message = message

    def __str__(self):
        return 'FloatError: ' + self.message


class StringError(Exception):
    def __init__(self, message='문자 말고 정수를 쓰세요'):
        self.message = message

    def __str__(self):
        return 'StringError: ' + self.message


def str_to_fraction(string):
    arr = string.split('/')
    if len(arr) != 2:
        raise ValueError('분수를 입력 해 주세요')
    elif not (arr[0].find('.') == -1 and arr[1].find('.')):
        raise FloatError
    elif not (all(c in '0123456789' for c in arr[0]) and all(c in '0123456789' for c in arr[1])):
        raise StringError
    else:
        try:
            numerator = int(arr[0])
            denominator = int(arr[1])
            return {
                'numerator': numerator,
                'denominator': denominator,
                'value': numerator / denominator,
                'string': string
            }
        except ValueError:
            raise ValueError('정수를 입력 하세요')


try:
    str_to_fraction('1//2')
except ValueError as v:
    print(v)
except FloatError as f:
    print(f)
except StringError as s:
    print(s)


try:
    str_to_fraction('1.1/2')
except ValueError as v:
    print(v)
except FloatError as f:
    print(f)
except StringError as s:
    print(s)


try:
    str_to_fraction('asbsadf/2')
except ValueError as v:
    print(v)
except FloatError as f:
    print(f)
except StringError as s:
    print(s)
  • Out
분수를 입력 해 주세요
FloatError: 소수 말고 정수를 쓰세요
FloatError: 문자 말고 정수를 쓰세요

마치며

예외 처리보다 안정적이고, 오류에 강한 프로그램을 만들 수 있게 끔 도와줍니다. 어떻게 보면, 예외 처리는 사용자, 혹은 협업하는 프로그래머 모두를 위한 우리가 할 수 있는 사소한 예의라고도 할 수 있겠습니다. 사용자가 뭘 잘못했는지 알아야 사용자가 프로그래머가 원하는 방향으로 프로그램을 실행 할 수 있고, 다른 협업하는 프로그래머가 해당 에러를 코드상으로 어떻게 처리 해야 하는지 알 수 있으니까요. 좋은 프로그래머가 되기 위해서, 예외 처리를 생활화 합시다! 이번 시간은 여기까지 입니다!