프로그래밍에서의 예외처리란?
프로그래밍에서의 예외란 우리가 작성한 코드에서 비정상적으로 발생하는 이벤트라고 말할 수 있다.
이러한 예외도 예측이 가능한 예외와 예측이 불가능한 예외로 나눌 수 있다.
예측 가능한 예외
- 로그인 처리 시 형식에 맞지 않는 이메일 값이 들어오는 경우
- 이름에 특수문자가 들어간 경우
예측 불가능한 예외
- 컴퓨터 메모리가 부족해서 생기는 이벤트 등의 OS에서 발생하는 문제
예외를 만났을 때 우리의 자세
예외는 반드시 처리해야하고, 로그 또한 반드시 남긴다!
문법적으로는 예외가 없지만, 코드 실행 프로세스(단계)발생하는 예외도 중요!
예외 종류
- SyntaxError
# 소괄호의 사용이 잘못된 경우
print('error'))
# if문의 :가 빠지고 들여쓰기가 잘못 된 경우
if True
pass
# ""으 사용이 잘못된 경우
print("error)
- TypeError
x = [1,2]
y = (1,2)
z = 'test'
print(x + y)
# TypeError: can only concatenate list (not "tuple") to list
print(x + z)
# TypeError: can only concatenate list (not "str") to list
print(y + z)
# TypeError: can only concatenate tuple (not "str") to tuple
리스트와 튜플, 리스트와 문자열, 튜플과 문자열은 더할 수 없으므로, 형변환을 거쳐야 한다.
print(x + list(y))
print(x + list(z))
>>> [1, 2, 1, 2]
>>> [1, 2, 't', 'e', 's', 't']
- NameError
# 참조가 없는 경우(선언되지 않은 것을 사용)
a = 10
b = 15
print(c)
- IndexError
x = [50, 70, 90]
# IndexError: list index out of range
print(x[4])
print(x.pop())
print(x.pop())
print(x.pop())
# IndexError: pop from empty list
print(x.pop())
- ValueError
시퀀스형 자료 안에서 데이트거 존재하지 않을 때 발생하는 에러.
x = [10, 50, 90]
x.remove(200)
# ValueError: list.remove(x): x not in list
- KeyError
dic = {'name': 'Lee', 'Age': 41, 'City': 'Busan'}
# KeyError: 'hobby'
print(dic['hobby'])
위와 같이 없는 key를 가져오는 경우 KeyError를 피하기 위해 None을 반환하는 get 방식을 추천한다.
print(dic.get('hobby'))
>>> None
- ZeroDivisionError
# 0으로 나눌 수 없음
print(100 / 0)
- AttributeError
module, class에 있는 잘못된 속성 사용으로 발생
# AttributeError: module 'time' has no attribute 'time2'. Did you mean: 'time'?
import time
print(time.time2())
- FileNotFoundError
파일을 찾을 수 없는 경우
f = open('test.txt')
# FileNotFoundError: [Errno 2] No such file or directory: 'test.txt'
그럼 예외 처리는 어떻게 처리하지?
Python에는 아래와 같은 예외 처리 문법이 있다.
try : 에러가 발생 할 가능성이 있는 코드 실행
except 에러명1: 여러개 가능
else : try 블록의 에러가 없을 경우 실행
finally : 항상 실행
사용방법에 대해서는 예제로 살펴보는 것이 좋다.
다음과 같은 성의 리스트가 있다.
name = ['Kim', 'Lee', 'Park']
예제 1
특정 값의 index를 가져오는 경우이다.
'Cho'라는 값이 없어 ValueError가 발생했고, 의도한 대로 에러가 발생했다는 문장이 출력 되었다.
try:
z = 'Cho'
x = name.index(z)
print('{} Found it! {} in name'.format(z, x + 1))
except ValueError:
print('Not found it! - Occurred ValueError!')
else:
print('Ok! else.')
>>> Not found it! - Occurred ValueError!
예제2
위 예제1에서 except 부분만 바꿨다.
try:
z = 'Cho'
x = name.index(z)
print('{} Found it! {} in name'.format(z, x + 1))
except:
print('Not found it! - Occurred ValueError!')
else:
print('Ok! else.')
>>> Not found it! - Occurred ValueError!
의도한 바와 같이 except 부분에 걸려 에러 메세지가 잘 출력 되었다.
하지만 pycharm에서 "Too broad exception clause "라는 Tip을 준다.
예외의 범위가 너무 광범위하다는 뜻이다.
실행하는 것에는 문제가 없지만 예1과 같은 정확한 에러 명시를 지향하자.
except:
# or
except Exception:
예제3
이번에는 예제2를 조금 더 보완해보자.
try:
z = 'Cho' # 'Cho'
x = name.index(z)
print('{} Found it! {} in name'.format(z, x + 1))
except Exception as e:
print(e)
print('Not found it! - Occurred ValueError!')
else:
print('Ok! else.')
finally:
print('Ok! finally')
>>> 'Cho' is not in list
>>> Not found it! - Occurred ValueError!
>>> Ok! finally
exception을 출력하여 광범위한 에러의 로그를 확실하게 남길 수 있다.
또, 마지막 finally는 무조건 실행하기 때문에 Ok!문이 출력된다.
예제4
그럼 이번엔 예외를 직접 설계해보자 😊
raise를 사용하여 예외를 만들 수 있다.
try:
a = 'Cho'
if a == 'Park':
print('OK! Pass!')
else:
raise ValueError
except ValueError:
print('Occurred! Exception!')
else:
print('Ok! else!')
>>> Occurred! Exception!
값이 없는 경우 ValueError를 만들고, 에러문을 출력했다.