🚪

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() — 블록 실행 시간을 자동 측정