목차
Exception
파이썬이 스크립트를 실행하고 처리할 수 없는 상황이면 다음과 같은 현상이 일어난다.
- 프로그램을 중지한다.
- Exception 이라는 특별한 종류의 데이터를 생성한다. 물론 Exception은 객체이다.
이 두가지 활동 모두 예외 발생(raising an exception) 이라고 한다.
Python은 코드에 어떤 작업이 필요한지 전혀 모를 때 항상 예외를 발생한다라고 할 수 있다.
다음으로 어떤 일이 벌어질까 ?
- 발생한 예외는 누군가 또는 무언가가 이를 알아차리고 처리할 것으로 기대한다.
- 발생한 예외에 대한 처리가 이루어지지 않으면 프로그램은 강제종료되고 Python에서 콘솔로 오류메세지를 보낸다.
- 그렇지 않을 경우, 예외가 적절히 처리되어 중단되었던 프로그램을 다시 시작할 수 있고, 실행을 계속할 수 있다.
파이썬은 예외를 관찰하고, 식별하고, 효율적으로 처리할 수있는 효과적인 도구를 제공한다.
모든 잠재적 예외에 명확한 이름이 부여되어 분류하고 적절하게 대응할 수 있기 때문에 가능하다.
다음과 같은 예외 오류 메세지를 이미 본 적 있을 수 있다.
IndexError : list index out of range
필연적으로, 우리는 예외를 발생시키는 코드를 작성하게 될 것이다.
그렇기 때문에 예외를 처리하는 방법을 아는 것이 매우 중요하다.
파이썬에는 63가지의 내장 예외가 있고, 이러한 예외는 트리 형태의 계층 구조로 표현된다.
가장 상위의 예외 클래스인 BaseException에서 상속된다.
또 다른 의미로는, 사용자가 고유한 특정 예외 클래스를 생성할 수 있음을 의미한다.
유일한 제약으로는 BaseException이나 다른 파생 예외 클래스를 서브클래싱 해야한다는 것이다.
(* 내장 예외의 갯수는 Python 버전마다 다를 수 있다.)
https://docs.python.org/3/library/exceptions.html
Built-in Exceptions
In Python, all exceptions must be instances of a class that derives from BaseException. In a try statement with an except clause that mentions a particular class, that clause also handles any excep...
docs.python.org
Exception handling
코드에서 예외가 발생할 가능성이 의심되는 경우
해당 try / except 코드 블록을 사용하여 "문제 발생 소지가 있는" 코드를 감싸야 한다.
실제로 예외가 발생하더라도 실행이 종료되지는 않지만,
해당 except 절 다음에 나오는 코드는 문제를 효과적으로 처리하려고 시도한다.
try:
print(int("a"))
except ValueError:
print("a는 정수형이 될 수 없다.")
문자 a를 정수 값으로 변홚려고 할 때 예외가 발생한다.
이 상황을 확장하여 보았을 때, int()의 경우 외부 데이터 소스를 받아 오는 경우 데이터 유형에 대해 완전히 신뢰할 수 없음으로
"문제 발생 소지가 있다" 라고 보고 try/except 블록으로 감싸는 것이 좋다.
Advanced exceptions : named attributes
이제 예외를 조금 더 자세히 살펴보고 해당 객체를 사용할 수 있는 방법에 대해 알아보자.
- ValueError
try:
print(int("a"))
except ValueError as e:
print("a는 정수형이 될 수 없다.")
print(e.args)
a는 정수형이 될 수 없다.
("invalid literal for int() with base 10: 'a'",)
예외 절에서 as 를 이용하여 객체의 변수을 지칭할 수 있다.
이 예에서는 e 가 예외 객체(인스턴스)를 대신한다. 해당 예외 인스턴스의 속성값을 통해 다양한 기능들을 볼 수 있다.
- ImportError
try :
import abcdef
except ImportError as e:
print(e.args)
print(e.name)
print(e.path)
("No module named 'abcdef'",)
abcdef
None
ImportError 같은 경우 코드를 보고 알 수 있듯, 모듈에 로드하는데 문제가 있을 때 발생한다.
속성은 다음과 같다.
ImportError_instance.name | 가져오려고 시도한 모듈의 이름을 나타낸다. |
ImportError_instance.path | 예외를 트리거한 파일의 경로 나타내며 없는 경우 None일 수 있다. |
- UnicodeError
try:
b'\x80'.decode("utf-8")
except UnicodeError as e:
print(e)
print(e.encoding)
print(e.reason)
print(e.object)
print(e.start)
print(e.end)
'utf-8' codec can't decode byte 0x80 in position 0: invalid start byte
utf-8
invalid start byte
b'\x80'
0
1
UnicodeError_instance.incoding | 오류 발생시킨 인코딩의 이름 |
UnicodeError_instance.reason | 특정 코덱 오류를 설명하는 문자열 |
UnicodeError_instance.object | 코덱이 인코딩 또는 디코딩을 시도한 객체 |
UnicodeError_instance.start | 객체의 유효하지 않은 데이터의 첫 번째 인덱스 |
UnicodeError_instance.end | 객체의 유효하지 않은 데이터 다음의 인덱스 |
chained exceptions
파이썬 3에서는 예외를 효과적으로 처리하기 위해 "Exception chaining"이라는 매우 흥미로는 기능을 도입하였다.
- 암시적 체이닝
이미 예외를 처리하고 있는데 우연히 추가 예외가 발생하는 상황이 있을 수 있다.
이전 예외에 대한 정보가 손실되지 않으면서, 오류가 발생한 코드 다음코드에서 해당 정보를 사용할 수 있어야한다.
이는 암시적 체이닝의 예시이다.
# 암시적 예외
a_list = ["first_error", "second_error"]
try:
print(a_list[2])
except IndexError as e:
print(0/0)
중간에 후속 추적을 결합하는 메세지가 포함된다.
During handling of the above exception, another exception occurred:
에러를 핸들링하는 와중에 다른 에러가 발생했다는 메모를 볼 수 있다.
# 암시적 예외
a_list = ["first_error", "second_error"]
try:
print(a_list[2])
except Exception as e:
try:
print(1/0)
except ZeroDivisionError as f:
print("Outer exception e :",e)
print("Inner exception f :",f)
print("Outer exception referenced :",f.__context__)
print("Is it the same object (f.__context__, e) :", f.__context__ is e)
Outer exception e : list index out of range
Inner exception f : division by zero
Outer exception referenced : list index out of range
Is it the same object (f.__context__, e) : True
except Exception : 절은 매우 광범위한 블럭으로 일반적으로 처리되지 않은 모든 예외를 포착하는 최후의 수단으로서 사용해야
한다. 어떤 종류의 예외가 발생할지 알 수 없기 때문에 프로그래머가 이후 코드를 바라보았을 때 의도를 파악하기 어렵기 때문이다.
바깥쪽 Exception 객체 e는 ZeroDivisionError 객체인 f.__context__에 의해 참조된다.
- 명시적 체이닝
또 다른 경우로는 의도적으로 예외를 처리하고 다른 유형의 예외로 변환하려는 경우이다.
이러한 상환은 한 코드의 통합 동작이 레거시 코드처럼 다른 코드와 유사하게 동작해야할 타당한 이유가 있을 때 일반적으로 발생한다.
이 경우 이전 예외의 세부 정보를 유지하는 것이 좋다.
이는 명시적 체이닝의 예시이다.
예를 들면 이런 것이 있을 수 있다.
로켓 발사 전 최종 확인 과정을 담당하는 코드를 생각해보면,
확인 목록은 매우 길고, 각 확인 항목마다 다른 예외가 발생할 수 있다.
하지만 매우 중요한 절차로, 모든 검사를 통과했는지 확인해야한다.
만약 하나라도 불합격하면 로켓은 발사할 수 없다.
class RocketNotReadyError(Exception):
pass
def person_check(crew_list):
try:
print("captain's name :", crew_list[0])
print("pilot's name :", crew_list[1])
print("mechanic's name :", crew_list[2])
print("navigator's name :", crew_list[3])
except IndexError as e:
raise RocketNotReadyError("Rocket Not Ready for takeoff") from e
crew_list = ["John", "Mary", "Mike"]
print("Final check procedure")
person_check(crew_list)
The above exception was the direct cause of the following exception:
중간에 에러가 연결되어, 직접적인 원인에 대해 설명하고 있다.
RocketNotReadyError의 예외 원인에 대해 파악하기 위해서, __cause__속성에 접근하면 다음과 같다.
class RocketNotReadyError(Exception):
pass
def person_check(crew_list):
try:
print("captain's name :", crew_list[0])
print("pilot's name :", crew_list[1])
print("mechanic's name :", crew_list[2])
print("navigator's name :", crew_list[3])
except IndexError as e:
raise RocketNotReadyError("Rocket Not Ready for takeoff") from e
crew_list = ["John", "Mary", "Mike"]
print("Final check procedure")
try:
person_check(crew_list)
except RocketNotReadyError as f:
print(f)
print(f.__cause__)
Final check procedure
captain's name : John
pilot's name : Mary
mechanic's name : Mike
Rocket Not Ready for takeoff
list index out of range
이번에는 안전하게 처리되었고, 해당 이유에 대한 print()도 함께 수행할 수 있었다.
- __context__ : 암묵적으로 연결된 예외에 속성으로 내재됨.
- __cause__ :명시적으로 연결된 예외에 속성으로 내재됨.
이러한 속성은 프로그래머가 나중에 로깅 등의 처리를 위해 원래 예외 객체에 대한 참조를 편리하고 일관된 방식으로 유지하는 데 도움이 된다.
traceback attribute
각 예외 객체는 __traceback__ 속성을 가지고 있다.
Python을 이용하면 각 예외 객체가 속성을 소유하므로 해당 속성(__traceback__)을 통해 추적정보를 알 수 있다.
import traceback #import
class RocketNotReadyError(Exception):
pass
def person_check(crew_list):
try:
print("captain's name :", crew_list[0])
print("pilot's name :", crew_list[1])
print("mechanic's name :", crew_list[2])
print("navigator's name :", crew_list[3])
except IndexError as e:
raise RocketNotReadyError("Rocket Not Ready for takeoff") from e
crew_list = ["John", "Mary", "Mike"]
print("Final check procedure")
try:
person_check(crew_list)
except RocketNotReadyError as f:
print(f.__traceback__)
print(type(f.__traceback__))
details = traceback.format_tb(f.__traceback__) #traceback.format_tb()
for detail in details:
print(detail)
Final check procedure
captain's name : John
pilot's name : Mary
mechanic's name : Mike
<traceback object at 0x79d5a32261c0>
<class 'traceback'>
File "/tmp/ipython-input-3-1177193041.py", line 19, in <cell line: 0>
person_check(crew_list)
File "/tmp/ipython-input-3-1177193041.py", line 13, in person_check
raise RocketNotReadyError("Rocket Not Ready for takeoff") from e
이전 예외 체이닝의 결과들을 보면,
RocketNotReadyError Traceback (most recent call last)
와 같은 표기를 볼 수 있다.해당 출력을 통해 우리는 추적 유형 객체를 처리해야한다는 결론을 낼 수 있다.traceback 모듈에서 제공되는 format_tb()라는 메서드를 통해 추적을 설명하는 문자열 목록을 가져올 수 있다.
경우에 따라 개발 프로젝트에서는 포괄적인 테스트 세션 이후에 기록된 추적 정보를 활용하여 통계를 수집하거나 버그 보고 프로세스를 자동화할 수도 있다.
체이닝 예외와, 추적 속성에 대한 자세한 내용은 아래를 참고한다.
https://peps.python.org/pep-3134/
PEP 3134 – Exception Chaining and Embedded Tracebacks | peps.python.org
This PEP proposes three standard attributes on exception instances: the __context__ attribute for implicitly chained exceptions, the __cause__ attribute for explicitly chained exceptions, and the __traceback__ attribute for the traceback. A new raise ....
peps.python.org
'Development > Python' 카테고리의 다른 글
[python] pickle 모듈을 이용한 객체 직렬화 (0) | 2025.06.26 |
---|---|
[python] 할당과 얕은 복사, 깊은 복사 (1) | 2025.06.25 |
[python] 내장 클래스의 속성 상속 (0) | 2025.06.24 |
[python] 구성 과 상속 (Composition, Inheritance) (1) | 2025.06.23 |
[python] 캡슐화 (Encapsulation) (0) | 2025.06.23 |