1. Python의 Context Manager 개념
Python의 Context Manager는
resource(자원, 리소스)를 안전하게 관리하기 위한 도구
(특정 메서드를 구현한 객체임).
일반적으로 file(파일), socket(네트워크 소켓), connection(데이터베이스 연결)과 같은 컴퓨터의 자원(resouce)를 사용할 때,
- 시작(or
open
)과 - 종료(or
close
) 작업을 명시적으로 처리해야 함.
Context Manager를 사용하면 이를 간단하고 안전하게 처리할 수 있음.
Context Manager는
with
statement와 함께 사용됨.- resource를 사용할 때,
open
과close
관련 정해진 작업들을 자동으로 수행할 수 있음.
2. Context Manager의 동작 원리
실제적으로 Context Manager는
다음의 특별한 메서드인 __enter__
와 __exit__
를 구현한 object 임.
__enter__
:with
문에 진입할 때 호출되는 method.- resource를 초기화하거나 설정 작업을 수행.
__enter__
method의 반환값은with
statement의as
키워드로 지정된 변수에 전달됨.
__exit__
:with
문을 벗어날 때 호출되는 method.- resource를 해제하여 OS에 돌려주는 작업을 수행.
2-1. __enter__
와 __exit__
의 매개변수 및 반환값
2-1-1. __enter__
:
Signature:
def __enter__(self):
- 매개변수: 없음 (
self
제외) - 반환값:
as
키워드로 지정할 resource handler 객체를 반환.- 보통
self
를 반환하지만, 필요한 경우 다른 객체도 반환 가능.
2-1-2. __exit__
:
Signature:
def __exit__(self, exc_type, exc_value, traceback):
- 매개변수:
exc_type
: 발생한 예외의 클래스 (예:ValueError
)exc_value
: 예외 인스턴스traceback
: 예외의 트레이스백 객체
- 반환값:
True
: 예외를 무시하고 계속 실행False
: 예외를 호출한 곳으로 전달
2-2. Examples 1: 파일 관리**
Python의 내장 open
함수의 반환값은 Context Manager로 동작함.
with open("example.txt", "w") as f:
f.write("Hello, world!")
# 파일은 자동으로 닫힘
open
함수의 Context Manager는- 파일을 열고(
__enter__
), - 작업이 끝난 후 파일을 닫음(
__exit__
).
3. 사용자 정의 Context Manager 만들기
직접 Context Manager를 만들려면 __enter__
와 __exit__
메서드를 구현한 class를 정의하면 됨.
3-1. 예제 2: 사용자 정의 Context Manager
class MyContext:
def __enter__(self):
print("Entering context...")
return self
def __exit__(self, exc_type, exc_value, traceback):
print("Exiting context...")
if exc_type:
print(f"An exception occurred: {exc_value}")
return True # 예외를 무시하려면 True를 반환, 아니면 False
# 사용
with MyContext() as ctx:
print("Inside context")
# raise ValueError("Something went wrong!") # 예외 테스트
__exit__
에서False
를 반환하면 발생한 예외가 계속해서 전달됨: 이후에 명시적 처리 없으면 crash로 이어지게 된다.
출력:
Entering context...
Inside context
Exiting context...
3-2. 예제 3: 실제 리소스 관리 예
class ManagedFile:
def __init__(self, filename):
self.filename = filename
self.file = None
def __enter__(self):
print(f"Opening file: {self.filename}")
self.file = open(self.filename, "w")
return self.file
def __exit__(self, exc_type, exc_value, traceback):
print(f"Closing file: {self.filename}")
if self.file:
self.file.close()
# 사용
with ManagedFile("test.txt") as f:
f.write("Managed file example.")
출력:
Opening file: test.txt
Closing file: test.txt
4. Context Manager 에서의 예외 처리.
Context Manager 에서의 예외처리를 위해선 __exit__
의 parameters를 이용하면 됨.
exc_type
,exc_value
,traceback
은__exit__
메서드에게 전달되는 arguments가 할당되는 parameters로,- 이를 통해 발생한 예외 정보를 확인하거나 처리할 수 있음.
단순히 이를 출력하거나 기록(logging)하는 작업 외에 좀 더 복잡한 처리 작업을 구현 가능함.
일반적으로 예외처리로 수행되는 구현은 다음으로 나누어짐:
- 예외 단순 출력하거나 로깅(logging)에 사용
- 조건부 예외 무시
- 예외 재처리
- 트레이스백 분석
4-1. exc_type
, exc_value
, traceback
의 역할
exc_type
: 발생한 예외의 클래스 (예:ValueError
,TypeError
등).exc_value
: 예외 인스턴스, 예외 객체 자체로 추가적인 정보(예: 에러 메시지)를 포함.traceback
: 예외가 발생한 트레이스백 객체로, 예외가 발생한 코드의 호출 스택 정보를 포함.
4-2. 예제: 예외 단순 출력
class MyContext:
def __enter__(self):
print("Entering context...")
return self
def __exit__(self, exc_type, exc_value, traceback):
if exc_type:
print(f"Exception type: {exc_type}")
print(f"Exception value: {exc_value}")
print(f"Traceback: {traceback}")
print("Exiting context...")
return False # 예외를 재발생 시키려면 False 반환
# 테스트
with MyContext() as ctx:
print("Inside context")
raise ValueError("Something went wrong!") # 일부러 예외 발생
출력:
Entering context...
Inside context
Exception type: <class 'ValueError'>
Exception value: Something went wrong!
Traceback: <traceback object at 0x7f1234567890>
Exiting context...
- 이 경우,
__exit__
는False
를 반환했으므로 예외가 다시 호출된 곳으로 전달됨.
4-3. 예제: 예외 무시하기
예외를 무시하고 프로그램을 계속 실행하려면 __exit__
에서 True
를 반환.
class MyContext:
def __enter__(self):
print("Entering context...")
return self
def __exit__(self, exc_type, exc_value, traceback):
if exc_type:
print(f"Exception occurred: {exc_value}, but it's ignored!")
print("Exiting context...")
return True # 예외 무시
# 테스트
with MyContext() as ctx:
print("Inside context")
raise ValueError("This error is ignored!")
print("Continuing program...")
출력:
Entering context...
Inside context
Exception occurred: This error is ignored!, but it's ignored!
Exiting context...
Continuing program...
4-4. 예제: 예외에 따라 다른 처리 수행하기
특정 예외만 무시하고,
다른 예외는 다시 발생시키는 방식도 가능함.
class MyContext:
def __enter__(self):
print("Entering context...")
return self
def __exit__(self, exc_type, exc_value, traceback):
if exc_type:
if exc_type is ValueError:
print(f"ValueError occurred: {exc_value}, but ignoring it.")
return True # ValueError 무시
else:
print(f"Other exception: {exc_value}. Propagating it.")
return False # 다른 예외는 재발생
print("Exiting context without exceptions.")
return True
# 테스트
try:
with MyContext() as ctx:
raise ValueError("Ignored error")
except Exception as e:
print(f"Caught an exception: {e}")
try:
with MyContext() as ctx:
raise TypeError("Unhandled error")
except Exception as e:
print(f"Caught an exception: {e}")
출력:
Entering context...
ValueError occurred: Ignored error, but ignoring it.
Exiting context without exceptions.
Entering context...
Other exception: Unhandled error. Propagating it.
Caught an exception: Unhandled error
4-5. traceback
활용하기
traceback
객체를 사용해 예외가 발생한 구체적인 위치를 추적할 수도 있음.
다음은 Python의 traceback
모듈을 사용하면 포맷팅된 트레이스백 정보를 출력하는 예제 코드임.
import traceback
class MyContext:
def __enter__(self):
print("Entering context...")
return self
def __exit__(self, exc_type, exc_value, traceback_obj):
if exc_type:
print("Exception details:")
print(f"Type: {exc_type}")
print(f"Value: {exc_value}")
print("Traceback:")
traceback.print_tb(traceback_obj) # 트레이스백 출력
print("Exiting context...")
return False
# 테스트
with MyContext() as ctx:
raise RuntimeError("Detailed traceback example")
출력:
Entering context...
Exception details:
Type: <class 'RuntimeError'>
Value: Detailed traceback example
Traceback:
File "example.py", line 25, in <module>
raise RuntimeError("Detailed traceback example")
Exiting context...
'Python' 카테고리의 다른 글
[Py] io.StringIO 와 io.BytesIO (0) | 2024.12.03 |
---|---|
[Py] Serialization of Python: pickle (0) | 2024.11.27 |
[Py] Higher-order Function (고차함수) (1) | 2024.11.20 |
[Py] 숫자 야구 게임: structured programming, type annotation, and OOP (0) | 2024.11.20 |
[DIP] Block Truncation Coding (BTC) (0) | 2024.11.18 |