컨텍스트 관리자는 코드 블록의 실행을 자동화하고 자원을 보장된 방식으로 정리할 수 있도록 도와줍니다. 예를 들어, 파일을 자동으로 닫거나, 데이터베이스의 트랜잭션 관리자를 해제하거나, 쓰레드를 잠그거나 잠금을 해제하는 것을 포함합니다. 파이썬의 표준에서는 이 패러다임이 외부 자원을 안전하고 편리하게 관리하기 위해 도입되었습니다.
이력:
컨텍스트 관리자 도입 이전에는 자원을 해제하기 위해 close()/release() 메서드를 명시적으로 호출해야 했으며, 이로 인해 예외가 발생할 경우 오류가 발생할 수 있었습니다. with ... as ...: 구문이 도입되면서, 파이썬은 "자원의 범위"를 명확히 정의하고 초기화 및 정리 메서드를 자동으로 호출할 수 있게 되었습니다.
문제:
자원의 "닫기"를 수동으로 관리하는 것은 위험합니다. close() (또는 release())를 잊으면 자원이 프로세스 종료 시까지 점유된 상태로 남거나 영원히 정지할 수 있습니다. 특히 파일, 네트워크 연결, 데이터베이스 트랜잭션 작업을 할 때 문제가 발생합니다.
해결:
컨텍스트 관리자는 마법의 메서드인 enter() 및 exit()를 통해 구현됩니다. with 블록에 들어갈 때 파이썬은 __enter__를 호출하고, 블록을 나갈 때는 __exit__를 호출합니다. 이때 블록 내부에서 예외가 발생하더라도 호출됩니다.
코드 예제:
class ManagedFile: def __init__(self, filename): self.filename = filename self.file = None def __enter__(self): self.file = open(self.filename, 'w') return self.file def __exit__(self, exc_type, exc_val, exc_tb): if self.file: self.file.close() # 예외를 억제할 수 있으며, return True; 일반적으로 return False with ManagedFile('demo.txt') as f: f.write('Hello, world! ') # 파일이 보장되게 닫힘
주요 특징:
같은 컨텍스트 관리자 인스턴스를 여러 번 연속으로 다른 with에서 사용할 수 있는가?
안 됩니다: 일반적으로 자원은 __enter__에서 열리고 __exit__에서 해제되므로 객체는 재사용에 부적절하게 됩니다. 각 with 블록에 대해 새 객체를 만드는 것이 좋습니다.
하나의 with에서 여러 자원을 사용해야 할 경우 어떻게 해야 하나요?
변수를 쉼표로 나누어 사용할 수 있습니다:
with open('a.txt') as fa, open('b.txt') as fb: ...
또는 복잡한 경우에 대해서는 contextlib.ExitStack을 사용할 수 있습니다.
enter/__exit__가 있는 클래스로 컨텍스트 관리자를 작성하는 것과 @contextmanager 데코레이터가 있는 제너레이터로 작성하는 것은 어떻게 다른가?
contextlib 모듈의 @contextmanager 데코레이터는 yield를 통해 관리자를 더 쉽게 구현할 수 있게 해줍니다:
from contextlib import contextmanager @contextmanager def open_file(name): f = open(name) try: yield f finally: f.close() with open_file('file.txt') as f: ...
개발자가 open()을 통해 파일을 열고 데이터를 쓰다가 close()를 호출하는 것을 잊으면, 파일이 열려 있는 상태로 남을 수 있습니다 (특히 오류가 발생할 경우), 데이터가 저장되지 않습니다.
장점:
단점:
모든 곳에서 with open()을 표준 (또는 사용자 정의 관리 자원)으로 사용하며, 컨텍스트 관리자가 자원 해제를 올바르게 수행합니다.
장점:
단점: