프로그래밍파이썬 백엔드 개발자

컨텍스트 관리자 및 contextlib 모듈의 @contextmanager 데코레이터를 통한 자원 관리에 대해 설명해 주세요: 필요성과 작동 원리, 그리고 주의할 점은 무엇인가요?

Hintsage AI 어시스턴트로 면접 통과

답변.

파이썬에서는 자원(파일, 연결, 트랜잭션)을 관리하기 위해 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')

주요 특징:

  • 블록 시작 — yield 전에 자원을 반환하는 부분.
  • 블록 종료 — yield 후; 반드시 예외 처리를 포함하고 닫기/해제를 수행해야 합니다.
  • 예외가 발생하더라도 자원을 닫는 보장이 있습니다.

트릭 질문.

@contextmanager에서 yield를 통해 여러 객체(예: 튜플)를 반환할 수 있습니까?

예, 관련 자원의 "그룹"을 반환하기에 편리합니다.

코드 예:

@contextmanager def managed_two(): a, b = [], {} try: yield a, b finally: a.clear(); b.clear()

yield 이후에 예외를 발생시키면 자원은 닫힐까요?

예, finally 블록은 항상 실행됩니다. with 내부에서 오류/예외가 발생하더라도 마찬가지입니다.

@contextmanager가 enter/__exit__가 있는 정식 클래스 컨텍스트 관리자를 대체할 수 있나요?

대부분의 단순한 경우에서는 가능하지만, 중첩된 상태나 상속이 있는 복잡한 경우에는 클래스를 사용하는 것이 더 편리합니다.

일반적인 오류 및 안티 패턴

  • finally 블록을 생략하면 오류 발생 시 자원이 누수될 수 있습니다.
  • yield가 하나뿐만 아니라면, 함수 내에 두 개의 yield가 있을 경우 RuntimeError가 발생합니다.
  • 일반 함수(여기에는 yield가 없음)와 @contextmanager를 사용하려는 시도는 실패합니다.

실생활 예시

부정적 케이스

파일을 수동으로 열고 닫습니다:

f = open('test.txt', 'w') try: f.write('Hello') finally: f.close()

장점:

  • 자원 생명 주기를 관리할 수 있는 완전한 제어 권한.

단점:

  • 많은 템플릿 코드가 필요하여 finally를 잊을 가능성이 더 높습니다.

긍정적 케이스

@contextmanager를 사용하여 작업 디렉토리를 임시로 변경하거나 파일(또는 환경 설정)을 엽니다:

@contextmanager def work_in(dirname): import os prev = os.getcwd() os.chdir(dirname) try: yield finally: os.chdir(prev)

장점:

  • 간결함, 원래 상태로 돌아올 것에 대한 보장.

단점:

  • 높은 수준의 추상화는 자원 관리 세부 사항을 감출 수 있습니다.