[Python] PEP 8: E722 do not use bare 'except'
Introduction
파이썬에는 PEP 8 (Python Enhancement Proposal #8) 이라는 스타일 가이드가 있다.
PEP 8 은 파이썬 코드를 깔끔하고 읽기 좋게 작성하기 위한 일련의 규칙들로, pycodestyle 과 같은 코드 스타일 분석 도구를 사용하면 쉽게 PEP 8 가이드를 따르고 있는 지 확인할 수 있다. (이런 툴들은 보통 vscode 같은 IDE에도 내장시켜 사용할 수 있다)
본인도 최근에서야 PEP 8 스타일 가이드 툴을 적용해 봤는데, 한 가지 이유가 궁금해지는 스타일 suggestion이 있었다.
E722 do not use bare 'except'
이 제안은 보통 다음과 같은 방식으로 예외를 처리할 때 발생한다.
try:
... # 오류 발생을 catch 하고 싶은 코드 블록
except:
... # 위의 블록에서 오류가 발생했을 시 실행되는 구문
위 코드에서는 try 블록에서 오류가 발생하면 (어떤 종류든) 바로 except 블록의 코드가 실행된다.
하지만 이런 식으로 코드를 작성하면 코드 분석기는 E722 do not use bare 'except' 라며 코드에 빨간 밑줄을 죽죽 긋는다.
사실 적당한 개인 연구나 소형 프로젝트에서는 적당히 위와 같은 식으로 적고 넘어갈 일이 많았기 때문에, 왜 이런 식으로 적으면 안 되는건지 문득 이유가 궁금해졌다.
Solution
공식 PEP 8 documentation에서 찾아본 결과, 이유는 다음과 같다.
추가적으로 예외 타입을 specify 하지 않고 bare except만을 사용해 예외 처리를 하는 경우, 위의 except: 는 except: BaseException 과 동일한 기능을 수행한다.
try:
... # 오류 발생을 catch 하고 싶은 코드 블록
except BaseException:
... # 위의 블록에서 오류가 발생했을 시 실행되는 구문
여기서 BaseException은 SystemExit / KeyboardInterrupt를 포함한 모든 파이썬 내장 예외를 하위 클래스로 두는 예외 클래스이며, 따라서 except: BaseException 은 앞의 두 예외를 포함한 모든 내장 예외 발생을 잡아낸다.
하지만 이럴 경우 KeyboardInterrupt 가 예외처리 되어버리기 때문에 만약 해당 예외처리 구간에서 루프를 돌고 있다면 프로그램을 Ctrl+C로 종료할 수가 없으며, 무엇보다도 어떤 종류의 에러든 다 처리하고 넘어가기 때문에 사후에 어떤 종류의 오류가 발생 했는 지 파악하기가 어려워진다는 문제가 있다.
from time import sleep
while True:
try:
print("Program Running...")
sleep(1)
except:
print("Exception occured")
위 코드는 Ctrl+C를 이용해 종료시킬 수 없다.. 물론 어떤 에러가 발생했는 지도 알 길이 없다.
따라서 except문 뒤에는 예외 타입을 비워두지 말고 항상 구체적인 예외 expectation을 명시해 예외 종류 별로 따로 처리 구문을 작성해 주는 것이 좋다.
정 그래도 예외 종류에 상관 없이 예외 처리를 하고 싶다면, except 뒤를 비워 두는 것 보다 except: Exception 의 형태로 작성하는 것을 추천한다.
Exception class는 SystemExit, KeyboardInterrupt와 같은 시스템 종료 이외의 모든 내장 예외를 하위 클래스로 가지는 예외 클래스로, 일반적으로 사용자 정의 예외 클래스를 작성할 때에도 이 클래스를 상속받도록 하는 것이 기본이다.
from time import sleep
while True:
try:
print("Program Running...")
sleep(1)
except Exception:
print("Exception occured")
이 코드는 적어도 Ctrl+C를 이용해 종료시킬 수 있다!
본인은 이렇게 어떤 종류의 에러든지 일단 잡고 봐야 하는 상황에서는, Exception 클래스에 대해 예외 처리를 시킨 후 예외 처리 블록에서 어떤 종류의 에러가 발생했는지 출력하도록 한다. 이렇게 하면 당장 안정적으로 코드를 돌릴 수 있으면서도 발생하는 에러 종류와 내용을 파악해 이후에 더 효과적인 예외 처리나 디버그를 수행할 수 있기 때문이다.
from time import sleep
while True:
try:
print("Program Running...")
sleep(1)
except Exception as e:
print(f"Exception occured:\n{e}")
모든 에러를 잡아내야 하는 경우에는 반드시 내용을 따로 로깅하거나 출력해 예상치 못한 에러에 대응하도록 하자.
Summary
요약하자면,
try, except를 이용한 파이썬 예외 처리는 되도록 예상되는 오류를 명시해 주는 편이 좋으며, 정 종류에 상관 없이 잡아내고 싶다면 except Exception: 의 형태로 작성하도록 하자. 이 때에는 반드시 발생한 에러의 내용을 기록해 두도록 하자.
아래는 참고할 만한 사이트들이다.
References
pycodestyle: Python style guide checker