컨텍스트 관리자는 with 블록에 들어가고 나오는 행동을 정의하는 객체로, 자원(파일, 연결 등)의 자동 관리를 제공합니다. 클래스 내에서 __enter__ 및 __exit__ 메서드를 사용하거나 contextlib 모듈의 @contextmanager 데코레이터를 통해 구현할 수 있습니다.
class FileManager: def __init__(self, filename, mode): self.filename = filename self.mode = mode self.file = None def __enter__(self): self.file = open(self.filename, self.mode) return self.file def __exit__(self, exc_type, exc_val, exc_tb): if self.file: self.file.close() with FileManager('test.txt', 'w') as f: f.write('Hello')
주의 사항: __exit__에서 예외를 제대로 처리하여 에러를 억제하기 위해 True를 반환하고, 자원을 열어두지 않도록 주의해야 합니다.
질문: with 블록 내에서 예외가 발생하면, Python은 __exit__ 메서드를 호출하나요? 에러 매개변수는 어떻게 전달되나요?
답변: 네, __exit__ 메서드는 항상 호출되며, 예외가 발생해도 호출됩니다. 이 때 예외 유형, 값 및 traceback이 전달됩니다. __exit__가 True를 반환하면 예외가 억제됩니다.
class Simple: def __enter__(self): print('Enter') def __exit__(self, exc_type, exc_val, exc_tb): print('Exit') print(exc_type, exc_val) return True # 오류가 "밖으로" 발생하지 않음 with Simple(): raise ValueError('boom!')
이야기
__exit__를 구현하는 것을 잊어버려서 예외가 발생했을 때 파일이 닫히지 않아 파일 디스크립터 누수와 많은 파일 작업 시 오류가 발생했습니다.이야기
__exit__에서 True를 반환했습니다. 이로 인해 오류가 "무시"되었고, 트랜잭션이 성공했다고 판단하여 데이터 무결성이 손상되었습니다.이야기
contextlib 모듈의 @contextmanager 데코레이터를 사용했지만, 코드가 실패할 때 yield 내부에서 예외를 처리하는 것을 잊어버려 소켓 연결이 닫히지 않아 서버가 열린 포트와 함께 "멈췄습니다."