항상 프로그램은 사용자의 실수든, 코드를 잘못 작성 했든, 원하지 않는 방향으로 흘러 갈 때가 있습니다. 그걸 방지 하기 위해선, 예외처리 라는 것이 필요 합니다. 예외처리는 프로그래밍의 세계에선 상당히 중요합니다. 프로그램의 비정상적인 종료를 방지하고, 우리의 프로그램 설계 상 무엇이 잘못되었는지 알 수 있게끔 해주는 척도이기도 합니다. Python
도 다른 언어와 마찬가지로, try
, except
키워드를 이용하여, 예외 처리를 실시 할 수 있습니다. 한 번 가볍게 알아 볼까요?
Python
에서 사용하는 try
, except
도 다른 언어에서 사용하듯 사용 하면 됩니다. try
문에서 예외처리 하고 싶은 코드를 작성 하고, except
문에서 try
문에서 작성한 코드가 에러를 발생 시켰을 때, 에러를 어떻게 처리 할 껀지 코드를 작성 하면 됩니다.
while True:
try: # 코드 작성
x = int(input("숫자를 입력 하세요: "))
break
except ValueError: # ValueError에 대한 특수한 예외 처리
print("이건 숫자가 아닌데... 다시 입력 하세요!")
일단 여러 가지 에러 객체들에 대해서 알아 보겠습니다. 일단... 엄청 많으니 주로 사용하는 에러 객체들을 위주로 설명 하겠습니다.
AssertionError
: assert 문이 제대로 작동하지 않을 때 발생합니다.IndexError
: 참조 하려는 인덱스가 범위를 벗어날 때 발생합니다.KeyError
: 참조 하려는 키가 기존 키 집합에서 찾을 수 없을 때 발생합니다.KeyboardInterrupt
: 사용자가 인터럽트 키(Control + C
, 혹은 Delete
)를 누를 때 발생하며, 모든 Exception을 잡는 코드에 의해 인터프리터가 종료하는 것을 막지 못하도록 Exception
상위에 있는 BaseException
을 직접 계승 합니다.MemoryError
: 메모리가 부족하지만, 가비지 컬렉터가 일부 객체의 삭제를 함으로써 복구될 수 있는 경우 발생합니다.NameError
: 참조하는 지역, 전역 변수 혹은 함수, 클래스 등을 찾을 수 없을 때 발생합니다.OSError
: 시스템 함수가 시스템 관련 에러를 돌려줄 때 발생합니다. (파일을 찾을 수 없거나, 디스크가 찼거나..)OverflowError
: 산술 연산의 결과가 너무 커서 표현 할 수 없을 때, 혹은 정수 범위를 벗어났을 때 발생합니다.RecursionError
: 최대 재귀 깊이가 초과하였을 때 발생합니다.TypeError
: 연산이나 함수가 부적절한 데이터 타입의 객체에 적용 되었을 때 발생합니다.ValueError
: 연산이나 함수가 부적절한 값을 가진 객체에 적용 되었을 때 발생합니다.ZeroDivisionError
: 나누기, 나머지 연산의 두 번째 인자가 0일 때 발생합니다.특정 에러만 핸들링하는 방법은, 에러 객체의 이름을 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("에러 처리 끝!")
프로그래머가 인위적으로 에러를 발생 시킬 수 있습니다. 바로, raise
문을 통해서 말입니다. 만약 여러분들이 파싱을 해주는 함수를 만들었는데, 이상한 값이 들어 왔다고 알려 주기 위해 에러를 발생 시킬 수도 있습니다. 그렇다면 상위에 존재하는 try ... except
문에서 에러를 처리 할 수 있겠죠?
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)
분수를 입력 해 주세요
정수를 입력 하세요
Exception
내의 메세지를 확인 하고 싶다면, except 에러 as e:
절 작성 후, 해당 절안에 e
를 출력 해 주면 됩니다.
try:
str_to_fraction('1/2')
except ValueError as v:
print(v)
Exception
객체를 직접 상속 받아, Error Class
를 만들 수 있습니다. 간단하게 클래스 생성 시, Exception
클래스를 상속 받으면 됩니다.
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)
분수를 입력 해 주세요
FloatError: 소수 말고 정수를 쓰세요
FloatError: 문자 말고 정수를 쓰세요
예외 처리는 보다 안정적이고, 오류에 강한 프로그램을 만들 수 있게 끔 도와줍니다. 어떻게 보면, 예외 처리는 사용자, 혹은 협업하는 프로그래머 모두를 위한 우리가 할 수 있는 사소한 예의라고도 할 수 있겠습니다. 사용자가 뭘 잘못했는지 알아야 사용자가 프로그래머가 원하는 방향으로 프로그램을 실행 할 수 있고, 다른 협업하는 프로그래머가 해당 에러를 코드상으로 어떻게 처리 해야 하는지 알 수 있으니까요. 좋은 프로그래머가 되기 위해서, 예외 처리를 생활화 합시다! 이번 시간은 여기까지 입니다!