Python:exceptions
Python에서, 모든 예외는 BaseException 에서 파생된 클래스의 인스턴스여야 합니다. 특정 클래스를 언급하는 except 절을 갖는 try 문에서, 그 절은 그 클래스에서 파생된 모든 예외 클래스를 처리합니다 (하지만 그것 이 계승하는 예외 클래스는 처리하지 않습니다). 서브 클래싱을 통해 관련되지 않은 두 개의 예외 클래스는 같은 이름을 갖는다 할지라도 결코 등등하게 취급되지 않습니다.
How to use
-
raise
는 에러를 발생시키기 위해 사용하는 명령어 이다. -
try: ... except: ...
는 가장 기본적인 Exception handling 구문이다.
raise from
- 8. 에러와 예외 — Python 3.10.2 문서
- (Python) raise vs. raise from e
- Stackoverflow - Python "raise from" usage
파이썬 except 블록에서 예외를 처리하는 도중에 또 다른 예외가 발생할 수 있다. 이 때 예외를 처리하는 도중 발생한 에러가 어떤 예외로부터 파생되었는지 알고 싶다면, raise from
을 사용하면 된다.
import json
def load_json_key(data, key):
try:
result_dict = json.loads(data) # ValueError 발생할 수 있음.
except ValueError as e:
raise KeyError(key) from e
else:
return result_dict[key] # KeyError 발생 가능
load_json_key('{"foo": "bar"', 'foo')
StackOverflow 에서 관련 내용을 찾을 수 있었다. from
절을 사용하면 발생한 예외의 __cause__
속성이 설정되어 에러 메시지 출력 시 "directly caused by" 를 통해 어떤 예외로부터 파생된 예외인지 알 수 있다는 것이다. 만약 from 절을 사용하지 않으면 __cause__
대신 __context__
속성이 설정된다. 그렇기 때문에 해당 예외가 어떤 상황에서 발생했는지를 알려주기 위해 에러 메시지 출력 시 "during handling ~ happened"와 같은 문구가 나오는 것이다.
만약 이 모든 메시지를 보는 것이 번거롭다면, raise … from None
을 사용하면 된다. 처음 발생한 예외만 에러 메시지로 출력된다.
List of exception
-
NotImplementedError
: 구현체가 존재하지 않음.
Handling Exceptions
import sys
try:
f = open('myfile.txt')
s = f.readline()
i = int(s.strip())
except IOError as e:
print "I/O error({0}): {1}".format(e.errno, e.strerror)
except ValueError:
print "Could not convert data to an integer."
except:
print "Unexpected error:", sys.exc_info()[0]
raise
User-defined Exceptions
class MyError(Exception):
def __init__(self, value):
self.value = value
def __str__(self):
return repr(self.value)
예외 계층 구조
내장 예외의 클래스 계층 구조는 다음과 같습니다:
BaseException
+-- SystemExit
+-- KeyboardInterrupt
+-- GeneratorExit
+-- Exception
+-- StopIteration
+-- StopAsyncIteration
+-- ArithmeticError
| +-- FloatingPointError
| +-- OverflowError
| +-- ZeroDivisionError
+-- AssertionError
+-- AttributeError
+-- BufferError
+-- EOFError
+-- ImportError
| +-- ModuleNotFoundError
+-- LookupError
| +-- IndexError
| +-- KeyError
+-- MemoryError
+-- NameError
| +-- UnboundLocalError
+-- OSError
| +-- BlockingIOError
| +-- ChildProcessError
| +-- ConnectionError
| | +-- BrokenPipeError
| | +-- ConnectionAbortedError
| | +-- ConnectionRefusedError
| | +-- ConnectionResetError
| +-- FileExistsError
| +-- FileNotFoundError
| +-- InterruptedError
| +-- IsADirectoryError
| +-- NotADirectoryError
| +-- PermissionError
| +-- ProcessLookupError
| +-- TimeoutError
+-- ReferenceError
+-- RuntimeError
| +-- NotImplementedError
| +-- RecursionError
+-- SyntaxError
| +-- IndentationError
| +-- TabError
+-- SystemError
+-- TypeError
+-- ValueError
| +-- UnicodeError
| +-- UnicodeDecodeError
| +-- UnicodeEncodeError
| +-- UnicodeTranslateError
+-- Warning
+-- DeprecationWarning
+-- PendingDeprecationWarning
+-- RuntimeWarning
+-- SyntaxWarning
+-- UserWarning
+-- FutureWarning
+-- ImportWarning
+-- UnicodeWarning
+-- BytesWarning
+-- ResourceWarning
개인적으로 사용하는 Exception 규칙
다음의 규칙을 따른다.
- 가급적 내장 예외를 사용한다.
- 가급적 구체적인 에러 메시지를 적는다.
- 문장 마침표는 사용하지 않는다.
- 개행은 가급적 사용하지 않는다. (에러 내용은 가급적 한줄로 요약)
- BaseException 은 빌트인이다. 따라서 커스텀 예외는 Exception 을 상속받는다. 상속 계층 구조를 참조하여 자식 클래스로부터 직접 상속받아도 OK.
몇 가지 케이스:
- 값이 잘못된 경우 (EmptyArgument, IllegalArgument, Encoding, Decoding, etc): ValueError
- 파싱에러의 경우 SyntaxError 사용금지. 이 에러는 파이선 문법에 한정된다.
- 상태와 관련된 에러 (Exists, NotExists, NotReadyState, AlreadyState, IllegalState, etc)는 모두 RuntimeError 이다.
- 찾을 수 없음 (NotFound) 에러도 RuntimeError 이다.
- 단, 파일을 찾을수 없다면 FileNotFoundError 를 사용해야 한다.
- 초기화 되지 않음 (NotInitialized) 에러도 RuntimeError 이다.
- LookupError는 (매핑 또는 시퀀스에 사용된) 키나 인덱스가 잘못되었을 때 사용된다.
See also
Favorite site
- 내장 예외 — Python 3.8.5 문서
- 8. Errors and Exceptions
- 파이썬 강좌 11편. 예외 처리 (Exception Handling)
- 4) 예외처리 - 점프 투 파이썬 1
References
-
Jump_to_Python_-_Exception.pdf ↩