🚪
Context Manager — with문이 실제로 하는 일
__enter__과 __exit__로 리소스 정리를 보장하는 프로토콜
with open('data.txt') as f:
content = f.read()
# 여기서 f는 이미 닫혀 있다 — 에러가 나도
try/finally를 안 써도 리소스 정리가 보장된다. 이게 context manager의 핵심.
내부 동작
# with obj as val: 은 이렇게 변환된다
val = obj.__enter__()
try:
# with 블록 내용
finally:
obj.__exit__(exc_type, exc_val, exc_tb)
__enter__가 리소스를 획득하고, __exit__가 정리한다. __exit__은 예외 정보를 인자로 받는다. True를 반환하면 예외를 삼키고, False면 예외를 다시 던진다.
contextlib.contextmanager
클래스를 만들기 귀찮으면 generator로 만들 수 있다:
from contextlib import contextmanager
@contextmanager
def timer():
start = time.time()
yield # 여기서 with 블록 실행
elapsed = time.time() - start
print(f'{elapsed:.2f}s')
with timer():
do_heavy_work()
yield 앞이 __enter__, yield 뒤가 __exit__. generator의 "실행 일시정지" 특성을 그대로 활용한 것이다.
DB 트랜잭션, 락 관리
DB 커넥션, 파일, 소켓, 스레드 락 — 반드시 정리해야 하는 리소스에 context manager를 쓰면 누수를 원천 차단한다.
핵심 포인트
1
with obj as val: → obj.__enter__() 호출 → val에 반환값 할당
2
with 블록 실행 (정상이든 예외든)
3
obj.__exit__(exc_type, exc_val, exc_tb) 호출 — 리소스 정리 보장
4
contextlib.contextmanager로 generator 기반 간편 구현 가능
사용 사례
파일/DB 커넥션 — 열고 닫기를 보장
timer() — 블록 실행 시간을 자동 측정