with절
•
자원을 획득하고, 사용하고, 반납할 때 주로 사용 (파일 열/닫기, DB 사용, 네트워크 연결)
1.
자원을 획득/생성한다.
2.
자원을 사용한다.
3.
자원을 반납/소멸한다.
•
파일을 여는 경우 다른 프로세스를 위해 사용한 뒤 닫아주어야 한다.
→ try / except / finally 구문을 통해 구현이 가능하지만 with절로 하는 것이 낫다.
•
예외가 발생하는 케이스 및 탈출 조건을 만족하는 케이스에 대해서 리소스를 정리하는 코드가 중복으로 작성
→ with절은 이런 불필요한 리소스를 with절 내에서만 엑세스가 가능하게 하고, 블록을 나가는 경우엔 어떤 이유든 간에 리소스를 해제하여 낭비를 막는 역할을 한다.
기본 구조
with 객체 [as 별칭]:
block..
# 빠져나오면 종료
Python
복사
예시
•
파일을 읽고 종료하는 코드
f = open('myFile.txt', 'w', encoding='utf8')
f.write("test")
f.close()
Python
복사
•
with절을 사용한 코드
with open('myFile.txt', 'w', encoding='utf8') as f:
f.write("test")
# 여기서 open('myFile.txt', 'w', encoding='utf8')가 CM
Python
복사
Context Manager
•
with 문과 함께 사용할 메서드를 정의하는 객체
file = open('file.txt', 'r')
try:
content = file.read()
finally:
file.close()
# 이랬던 코드가
with open('file.txt', 'r') as file:
content = file.read()
# 이렇게 간단해진다.
Python
복사
•
구현 방법
1.
Native Implementation
2.
conextlib.contextmanager
3.
contextlib.ContextDecorator
1. Native Implementation
•
__enter__ 및 __exit__ 메서드가 존재
•
자원의 설정 및 정리를 수동으로 처리하는 것은 오류 발생 가능성이 높아지기 떄문에 자동으로 관리해주는 Context Manager가 필요하다.
•
클래스로 활용도 높히기
class Hello:
def __enter__(self):
print('enter...')
return self
def sayHello(self, name):
print('hello ' + name)
def __exit__(self, exc_type, exc_val, exc_tb):
print('exit...')
with Hello() as h:
h.sayHello("name")
# 출력 결과
enter...
hello name
exit...
Python
복사
•
__enter__(self)
◦
with블록 시작 시 호출
•
__exit__(self, exc_type, exc_val, exc_tb)
◦
with 블록 종료 시 호출
2. conextlib.contextmanager
•
contextlib 내장 모듈의 contextmanager 데코레이터를 이용하는 방법
•
함수 리팩토링이 쉬워진다.
객체지향적으로 프로그램을 설계하면 아무 의미 없는 가짜 부모클래스를 만들어야하는 번거로움이 있다.
•
__enter__ == yield 이전 코드
•
__exit__ == yield 이후 코드
from contextlib import contextmanager
@contextmanager
def gen_process():
print('start of process')
yield 'gen_process'
print('end of process')
with gen_process() as gp:
print(f'processing {gp} ...')
Python
복사
3. contextlib.ContextDecorator
•
일종의 믹스(1번 + 2번)인 클래스로 데코레이터를 구현하기 위한 로직을 제공
•
process 함수와 process_decorator 데코레이터 사이의 독립성이 보장
•
as 등으로 무언가를 넘겨주는 작업도 없다.
불편할 수도 있지만 기능적으로 분리되고 접근을 제한하여 독립성을 보장하는 것은 좋은 특성
from contextlib import ContextDecorator
class process_decorator(ContextDecorator):
def __enter__(self):
print('start of process')
return self
def __exit__(self, exc_type, exc_value, exc_traceback):
print('end of process')
@process_decorator()
def process():
print('processing ...')
Python
복사
time.timer을 사용하여 실행시간 측정하기
import time
class Timer:
def __enter__(self):
self.start = time.time()
return self
def __exit__(self, exc_type, exc_val, exc_tb):
self.end = time.time()
print(f'경과 시간: {self.end - self.start} 초')
Python
복사
with와 try-finally의 차이점
•
둘다 Python에서 리소스 관리에 사용
•
try-finally 블록
◦
예외가 try 블록에서 발생했는지 여부에 관계없이 항상 실행되어야 하는 정리 코드를 지정할 수 있는 좀 더 낮은 수준의 구조
◦
리소스의 설정을 관리하는 메커니즘을 제공하지 않음.
•
with 블록
◦
리소스의 설정과 정리를 모두 처리
◦
리소스 관리 로직을 클래스 내에서 캡슐화