context manager는 설정과 정리 로직을 정의하는 객체로, with 문과 함께 사용됩니다. 오류가 발생하더라도 정리(파일 닫기, 락 해제, 트랜잭션 롤백)가 자동으로 일어나도록 보장합니다.
이것이 해결하는 문제
python
f = ()
data = f.read()
process(data)
f.close()
() f:
data = f.read()
process(data)
with 블록은 블록이 정상적으로 종료되든 예외를 통해 종료되든 f.close()가 실행되도록 보장합니다 — 잊혀지거나 건너뛴 정리로 인한 자원 누수를 제거합니다.
with open("f.txt") as f: ... # 파일 (자동 닫기)
with lock: # 스레딩 락 (자동 해제)
shared_resource += 1
with db.transaction(): # DB 트랜잭션 (자동 커밋/롤백)
...
with open("a") as a, open("b") as b: # 한 번에 여러 개
...
class Timer:
def __enter__(self): # 설정 — `with` 진입 시 실행
self.start = time.perf_counter()
return self # `as`에 바인딩되는 값
def __exit__(self, exc_type, exc_val, tb): # 정리 — 종료 시 실행 (오류 시에도)
print(f"took {time.perf_counter() - self.start:.3f}s")
# False 반환 → 예외를 전파; True → 억제
with Timer():
do_work()
context manager는 __enter__(설정)와 __exit__(정리, 예외 정보를 받음)를 구현합니다.
from contextlib import contextmanager
@contextmanager
def timer():
start = time.perf_counter()
try:
yield # yield 이전의 모든 것 = 설정; 이후 = 정리
finally:
print(f"took {time.perf_counter() - start:.3f}s")
with timer():
do_work()
@contextmanager decorator는 generator를 context manager로 전환합니다 — yield 이전은 설정, 이후는 정리(안전을 위해 finally에서).
context manager와 with는 자원 관리를 안전하게 처리하는 Pythonic한 방법입니다 — 예외가 발생하더라도 파일이 닫히고, 락이 해제되고, 연결이 반환되고, 트랜잭션이 마무리되도록 보장합니다.
이는 자원 누수와 일관성 없는 상태를 예방하며, 이는 수동 정리에서 흔한 버그의 원인입니다.
이들은 끊임없이 사용되며(모든 파일 연산은 with를 사용해야 함), 직접 작성하는 방법(__enter__/__exit__ 또는 더 간단한 @contextmanager)을 알면 모든 설정/정리 패턴을 깔끔하고 안정적으로 캡슐화할 수 있습니다.