2.24. 컨텍스트 관리자¶
with 문은 설정 코드, 본문, 그리고 정리 코드를 순서대로 실행합니다 – 본문이 도중에 실패하더라도 정리 코드가 반드시 실행된다는 보장과 함께 말이죠. 설정과 정리를 제공하는 한 쌍의 메서드를 컨텍스트 관리자 라고 합니다.
형태는 다음과 같습니다:
with <expression> as <name>:
<body>
표현식은 컨텍스트 관리자를 반환합니다. Python은 그것의 __enter__ 메서드를 호출하고, 선택적으로 결과를 <name> 에 바인딩한 다음 본문을 실행하고, 그 후 __exit__ 를 호출합니다 – 본문이 정상적으로 완료되었든 예외를 발생시켰든 상관없이 말이죠.
__exit__ 는 블록 안에서 무슨 일이 일어나든 블록의 끝에서 실행됩니다.¶
2.24.1. 컨텍스트 관리자 사용하기¶
대표적인 예는 파일 열기입니다:
with open("data.txt") as f:
text = f.read()
# f is now closed, even if read() failed
open() 은 그 자체로 컨텍스트 관리자인 파일 객체를 반환합니다. __enter__ 는 파일을 반환하고 __exit__ 는 파일을 닫습니다. with 블록은 “끝나면 항상 파일을 닫는다”를 호출자가 기억해야 하는 것이 아니라 기본 동작으로 만듭니다.
2.24.1.1. 여러 개의 컨텍스트 관리자¶
하나의 with 문은 쉼표로 구분하여 여러 컨텍스트 관리자를 한 번에 진입할 수 있습니다:
with open("input.txt") as src, open("output.txt", "w") as dst:
dst.write(src.read())
두 개의 중첩된 with 블록과 동등하지만 더 평평합니다. 관리자들은 왼쪽에서 오른쪽으로 진입하고 역순으로 종료됩니다. 오른쪽 관리자의 __enter__ 가 예외를 발생시키면 왼쪽 관리자의 __exit__ 는 여전히 실행됩니다.
2.24.2. 컨텍스트 관리자 작성하기¶
__enter__ 와 __exit__ 메서드를 가진 모든 클래스는 컨텍스트 관리자로 작동합니다:
class Section:
def __init__(self, label):
self.label = label
def __enter__(self):
print("---", self.label, "---")
return self
def __exit__(self, exc_type, exc_value, traceback):
print("--- end", self.label, "---")
return False
with Section("setup"):
print("doing the work")
출력:
--- setup ---
doing the work
--- end setup ---
__exit__ 는 블록을 끝낸 예외를 설명하는 세 개의 인수를 받거나, 블록이 정상적으로 완료된 경우 세 개의 None 값을 받습니다. False (또는 None)를 반환하면 정리 후 모든 예외가 전파되도록 하고, True 를 반환하면 예외를 삼킵니다.
“열기 / 닫기” 또는 “획득 / 해제” 생명주기를 가진 모든 리소스에 컨텍스트 관리자를 사용하세요 – 파일에만 국한되지 않습니다. 이 패턴은 정리를 설정과 짝지어 둘 다 도입되는 지점에 두므로, 긴 함수 중간에서 닫기를 잊더라도 리소스가 누수될 수 없습니다.