Context Manager & Decorator

EBAB!·2024년 1월 17일
0

OS가 사용할 수 있는 자원은 한정되어 있기에 우리는 할당을 받았다면 다시 돌려줘야 원활한 자원 순환이 가능하다.

간단하게는 다음과 같은 코드가 있다.

file = open('./test.txt', 'w')
try:
	file.write('test text\nend.')
finally:
	file.close()

이렇게 file.close()를 해서 자원을 돌려줘야 OS는 다시 자원을 다른 곳에 할당해줄 수 있다.

흔히 사용하는 다음 코드는 자동으로 자원을 반환한다.

with open('./test.txt', 'w') as file:
	file.write('test text\nend.')

Context Manager

컨텍스트 매니저는 리소스 관리를 위한 구조로, 주로 파일, 네트워크 연결, 데이터베이스 세션과 같은 리소스의 할당 및 해제를 자동화하는 데 사용된다.
위에서 볼 수 있듯이 with 문과 함께 사용되며, 이를 통해 코드의 가독성과 안정성을 높일 수 있다.

컨텍스트 매니저는 __enter__, __exit__ 두 개의 매직 메서드를 구현한다.

__enter__

with 문이 시작될 때 실행된다. 여기서 리소스를 할당하거나 초기화하는 코드를 작성할 수 있고, with 문과 함께 사용되는 변수에 할당될 값을 반환할 수 있다.

__exit__

with 문이 끝날 때 실행된다. 인자로 (예외 유형, 예외 값, 백트레이스) 세 개의 인자를 받는다.
여기서는 리소스의 정리 작업을 수행하고, 예외가 발생한 경우 이 메서드 내에서 처리할 수 있으며, 예외를 무시하려면 True를 반환하면 된다.

class MyFileWriter():
	def __init__(self, filename, method):
    	print("init")
        self.file_obj = open(file_name, method)
	def __enter__(self):
    	print("enter")
        return self.file_obj
    def __exit__(self, exc_type, value, trace_back):
    	print("exit")
        if exc_type:
        	print("exc!")
		self.file_obj.close()

# Custom
with MyFileWriter('./test.txt', 'w') as f:
	f.write('test text\nend.')

# Origin
with open('./test.txt', 'w') as file:
	file.write('test text\nend.')

위 코드에서 보듯이 context manager을 통해 커스텀한 방식으로 파일을 다룰 수 있다.

만약 어떤 함수의 실행시간을 측정하려 할 때, time 함수 대신 다음 context manager 객체를 만들 수 있다.

import time

class ExcuteTimer():
	def __init__(self, msg):
    	self._msg = msg

	def __enter__(self):
    	self._start = time.monotonic()
        return self._start

	def __exit(self, exc_type, exc_val, exc_traceback)
    	if exc_type:
        	print("exception!")
		else:
        	print(f'{self._msg}: {time.monotonic() - self._start} sec'
		return True
        

with ExcuteTimer("function excute time") as v:
	print(v)  # __enter__ 메서드의 반환값
    # 시간을 측정하고 싶은 함수나 코드 . . .
	for i in range(10**8):
    	pass


Context Manager Decorator

파이썬의 표준 라이브러리 중 하나이다.
위에서 매직 메서드를 이용하여 구현하던 부분을 데코레이터 형식으로 지원하여 좀 더 쉽게 context manager를 구현할 수 있다.

import contextlib
import time


@contextlib.contextmanager
def ExcuteTimerDe(msg):
    start = time.monotonic()
    try:
        yield start  # __enter__
    except BaseException as e:
        print(f"logging: {e}")
    else:  # __exit__
        print(f"{msg} : {time.monotonic() - start} sec")


with ExcuteTimerDe("function excute time") as v:
	print(v)
	for i in range(10**8):
    	pass
	# raise ValueError("error msg")가 있고 이 부분이 발생한다면 ExcuteTimerDe의 에러문구로 출력된다.

이것은 위에서 클래스로 선언한 타이머와 완전히 동일한 기능을 한다.

contextlib.contextmanager을 데코레이터로 한 함수는 yield의 시작을 __enter__, 이후의 코드를 __exit__로 구분한다.

profile
공부!

0개의 댓글