파이썬에서 with 문은 런타임 컨텍스트(runtime context)를 지원하는 핵심 구문입니다. 이 컨텍스트는 컨텍스트 관리자(context manager)에 의해 정의되며, 컨텍스트 관리자를 활용하면 사용자가 직접 클래스를 만들어 런타임 컨텍스트를 정의할 수 있습니다.
컨텍스트 관리자는 with 문의 본문(body)이 실행되기 전에 진입하여 작업을 시작하고, 본문의 실행이 끝나면 자동으로 종료됩니다. 덕분에 파일 열기/닫기처럼 리소스를 반드시 해제해야 하는 작업을 안전하게 처리할 수 있습니다.
컨텍스트 관리자의 두 가지 핵심 메서드
컨텍스트 관리자 프로토콜을 구현하려면 다음 두 가지 특수 메서드(special method)를 정의해야 합니다.
1. __enter__() 메서드
__enter__() 메서드는 런타임 컨텍스트에 진입할 때 호출됩니다. 이 메서드는 현재 객체(self) 또는 그와 관련된 객체를 반환하며, 반환된 값은 with 문의 as 절에 있는 식별자(identifier)에 바인딩됩니다.
2. __exit__(exc_type, exc_val, exc_tb) 메서드
__exit__() 메서드는 런타임 컨텍스트를 종료할 때 호출되며, 불리언(Boolean) 타입의 결과를 반환합니다. 이 반환값은 예외 발생 여부를 나타냅니다.
- True 반환: with 문 내에서 예외가 발생했더라도 예외가 외부로 전파되지 않고 정상적으로 처리된 것으로 간주됩니다.
- False 반환(또는 None): with 문에서 예외가 발생하면 해당 예외가 그대로 전파되며, finally 블록과 유사한 방식으로 마무리 코드가 실행됩니다.
예제 코드
아래 예제는 파일을 안전하게 열고 닫는 사용자 정의 컨텍스트 관리자 클래스입니다.
class MyFileManager:
def __init__(self, fname):
self.file_name = fname
def __enter__(self):
self.myFile = open(self.file_name, 'r')
return self.myFile
def __exit__(self, exc_type, exc_val, exc_tb):
if self.myFile:
self.myFile.close()
with MyFileManager('sampleTextFile.txt') as myFile:
x = myFile.read()
print(x)실행 결과
Test File.
We can store different contents in this file
~!@#$%^&*()_+/*-+\][{}|:;"'<.>/,'"]코드 설명
- __init__: 객체 생성 시 파일 이름을 인스턴스 변수로 저장합니다.
- __enter__: with 문이 시작되면 파일을 읽기 모드(
'r')로 열고, 파일 객체를 반환합니다. 이 객체가as myFile에 바인딩됩니다. - __exit__: with 블록이 끝나면(예외 발생 여부와 무관하게) 파일을 자동으로 닫아 리소스 누수를 방지합니다.
이처럼 컨텍스트 관리자를 직접 구현하면 try-finally 구문 없이도 깔끔하고 안전한 리소스 관리가 가능합니다. 실무에서는 파이썬 표준 라이브러리의 contextlib.contextmanager 데코레이터를 활용하면 제너레이터 기반으로 더욱 간결하게 컨텍스트 관리자를 작성할 수도 있습니다.