파이썬에서는 자원(파일, 연결, 트랜잭션)을 관리하기 위해 with 문을 사용하며, 이는 컨텍스트 관리자 프로토콜(enter, exit)에 기반합니다. 간단한 경우에는 전체 클래스를 작성하는 것이 과도하므로, 자원 관리자를 생성자로 정의할 수 있게 해주는 @contextmanager 데코레이터(콘텍스트 라이브러리 모듈)가 제안되었습니다.
문제 — 자원을 수동으로 해제하거나 닫는 것은 번거롭고, 코드가 오류에 취약해집니다(예: 파일을 닫는 것을 잊음). 또한 간단한 작업(예: 임시 디렉터리 변경 또는 stdout 변경)을 위해 별도의 클래스를 작성하고 싶지 않습니다.
해결책 — @contextmanager를 사용하여 자원의 사용 "시작"과 "종료"를 간결하게 설명하고, 예외 처리를 보장하며 자원을 해제합니다.
코드 예:
from contextlib import contextmanager @contextmanager def open_file(filename, mode): f = open(filename, mode) try: yield f finally: f.close() with open_file('test.txt', 'w') as f: f.write('Hello')
주요 특징:
@contextmanager에서 yield를 통해 여러 객체(예: 튜플)를 반환할 수 있습니까?
예, 관련 자원의 "그룹"을 반환하기에 편리합니다.
코드 예:
@contextmanager def managed_two(): a, b = [], {} try: yield a, b finally: a.clear(); b.clear()
yield 이후에 예외를 발생시키면 자원은 닫힐까요?
예, finally 블록은 항상 실행됩니다. with 내부에서 오류/예외가 발생하더라도 마찬가지입니다.
@contextmanager가 enter/__exit__가 있는 정식 클래스 컨텍스트 관리자를 대체할 수 있나요?
대부분의 단순한 경우에서는 가능하지만, 중첩된 상태나 상속이 있는 복잡한 경우에는 클래스를 사용하는 것이 더 편리합니다.
파일을 수동으로 열고 닫습니다:
f = open('test.txt', 'w') try: f.write('Hello') finally: f.close()
장점:
단점:
@contextmanager를 사용하여 작업 디렉토리를 임시로 변경하거나 파일(또는 환경 설정)을 엽니다:
@contextmanager def work_in(dirname): import os prev = os.getcwd() os.chdir(dirname) try: yield finally: os.chdir(prev)
장점:
단점: