이펙티브 파이썬 Item 20. None을 반환하기보다는 예외를 발생시켜라
- 프로그래밍 언어/파이썬
- 2023. 10. 18.
요약
- None을 반환하면 조건문에서 0, "", [] 같은 것들을 반환할 때와 동일하게 판단되므로 실수하기 쉬워짐.
- None을 반환한다면 다음 형태로 처리하는 것이 좋음.
- 명시적으로 None 체크하기 (is None) → 타입체커랑 사용하기 좋음.
- (연산 성공, 결과)를 튜플 형식으로 반환하기 → 사용자가 무시하기 쉬움.
- 명시적으로 예외를 던지고 Try / except / else를 사용하기
- 사용자가 문서를 읽지 않으면 런타임 에러에 대응하지 못할 수 있음.
- 그러나 빠른 실패라는 관점에서 좀 더 단단한 어플리케이션이 될 수도 있음.
Item 20. None을 반환하기보다는 예외를 발생시켜라
if None:
print('false'
if 0:
print('false')
if []:
print('false')
None을 반환하기 보다는 예외를 발생시켜야 하는 이유는 실수할 여지가 있기 때문이다. 예를 들어 위 값들은 항상 False를 반환한다. 파이썬의 조건문 관점에서 None, 0, []은 False의 대상으로 보기 때문이다.
def func(a, b):
try:
return a/b
except ZeroDivisionError:
return None
if not func(5,0):
print('false')
if not func(0,5):
print('false')
위의 경우가 바로 대표적인 실수 케이스다.
- 5/0은 ZeroDevisionError가 발생해서 None이 리턴된다.
- 0/5는 정상적으로 나누기 되어 0이 리턴된다.
그러나 If 문은 None과 0 둘다 False로 판단하게 된다. 개발자가 스마트하게 처리를 하려했으나 오히려 실수할 여지를 만들어 버리게 된 것이다. 이런 것을 해결하려면 어떻게 해야할까?
해결방법
- None 체크를 한다.
- (연산 성공, 성공값) 튜플을 반환한다.
- 항상 에러를 던지고 Try ~ except ~ else ~ finally 문을 사용한다.
각각의 방법에는 장단점이 있고, 그것을 잘 파악해서 사용하는 것이 좋을 것 같다.
1. None 체크를 한다.
from typing import Optional
def func(a, b) -> Optional[float]:
try:
return a/b
except ZeroDivisionError:
return None
result = func(5, 0)
if result is None:
print('result is None')
else:
print('do your action.')
- 첫번째 방법으로는 명시적으로 None 체크를 하는 방법이 있다. (is None)
- 이 방법은 타입힌트를 사용할 때에 적극적으로 사용할 수 있는 방법이다.
Optional을 반환했을 때 None 체크를 명시적으로 해주지 않는다면 타입 체커는 문제가 있는 코드로 판단한다. 따라서 타입체크와 타입힌트(Optional)를 적극적으로 사용할 때, None을 반환할 일이 있다면 is None 같은 코드로 None을 명시적으로 체크해주는 방법이 있다.
그러나 If 문이 존재하므로 시각적인 잡음이 존재한다는 생가이 들 수도 있을 것이다. 또한, 타입체크를 사용하지 않는 경우에는 개발자가 명시적으로 None에 대응하지 않을 수도 있기 때문에 런타임에 Null Pointer Exception이 발생할 수도 있다.
2. 계산 성공 유무를 함께 반환
def func(a, b):
try:
return (True, a/b)
except ZeroDivisionError:
return (False, None)
success, value = func(5, 0)
if success:
print('do your action.')
- 단순히 None만 반환하는 경우 None과 0의 차이를 조건문에 판단할 수 없기 때문에 실수할 여지가 많아진다.
- 이 때, 계산이 성공적으로 끝났는지를 판단할 수 있는 변수를 함께 리턴하는 방법이 있다.
이 때의 문제점은 다음과 같다.
- if 조건문이 하나 더 들어가야 한다. → 시각적 잡음.
- Optional + 타입체커를 이용하는 경우 None 타입 체크가 한번 더 들어가야한다. → 어차피 None 체크해야 함.
- 사용자들이 success의 의미를 모르거나, 무시하고 코드를 작성할 수 있다. → 안전하지 못함.
3. None 대신에 예외를 던진다.
def func(a, b) -> float:
try:
return a/b
except ZeroDivisionError:
raise ValueError('잘못된 입력. 0으로 나눌 수 없음.')
try:
value = func(5, 0)
except ValueError as e:
print(f'잘못된 입력: {e}')
else:
print('do your action.')
None을 반환하는 대신 명시적으로 예외를 던지는 방법이다.
- Optional을 사용하지 않기 때문에 타입체커를 사용하기도 편리하다. (If문을 하나 줄임)
- None 값이 없기 때문에 None 체크를 하지 않아도 되어 시각적 잡음이 없다.
하지만 마찬가지로 단점이 존재한다고 생각한다
- 파이썬에는 체크 예외가 없다. 문서를 읽지 않는 사람이라면 런타임 에러가 발생해서 프로그램이 종료될 수 있다. 하지만 빠른 실패로 어플리케이션 전체에 예외가 퍼지는 것을 막을 수 있음.
- try, except, else의 시각적 잡음도 if를 한번 더 하는만큼 큰 것 같음.
'프로그래밍 언어 > 파이썬' 카테고리의 다른 글
이펙티브 파이썬 Item 21. 변수 영역과 클로저의 상호작용 방식을 이해하라 (1) | 2023.10.19 |
---|---|
이펙티브 파이썬 Item 65. try/except/else/finally의 각 블록을 잘 활용하라 (0) | 2023.10.18 |
Effective Python Item 22. 변수 위치 인자를 사용해 시각적인 잡음을 줄여라 (0) | 2023.10.15 |
Effective Python Item 19. 함수가 여러 값을 반환하는 경우 절대로 네 값 이상을 언패킹하지 마라. (0) | 2023.10.13 |
단단한 파이썬 9. 데이터 클래스 (0) | 2023.10.09 |