상세 컨텐츠

본문 제목

Context Manager

개발/python-객체지향프로그래밍(OOP)

by Matthew0633 2022. 5. 27. 23:57

본문

Context Manager 개념

context manager 란 특정 행동을 할 때 항상 일정한 런타임 환경을 만들어주기 위해 특정 행동에의 진입과 종료를 관리해주는 객체 이다.

with 구문을 통해 실행되어, 원하는 타이밍에 정확하게 리소스를 할당하고 제공하는 역할을 한다.

(python 공식 document) A context manager is an object that defines the runtime context to be established when executing a [with] statement. The context manager handles the entry into, and the exit from, the desired runtime context for the execution of the block of code. Context managers are normally invoked using the with statement , but can also be used by directly invoking their methods.

with 구문을 사용하지 않는다면, “파일쓰기”를 실행할 때 마지막에 close 를 꼭 명시해주어야 리소스가 반환되기 때문에 아래와 같이 코드를 작성해야하는 번거로움이 있다.

# Ex1
# No use with.

file = open('./testfile1.txt', 'w')
try:
    file.write('Context Manager Test1.\nContextlib Test1.')
finally:
    file.close()

그러나 with 구문을 사용하면 close() 가 자동으로 실행되어 해당 구문을 실행하는데 사용된 리소스가 자동으로 반환될 수 있다.

# Ex2
# Use with.

with open('testfile2.txt', 'w') as f:
    f.write('Context Manager Test2.\nContextlib Test2.')

 

Class를 활용한 Context Manager 객체 정의

with 구문 내에서 사용할 수 있는 객체를 우리가 Class 를 사용해서 직접 정의할 수 있다.

__enter__ 과 __exit__ 매직메소드를 활용하면, context manager 로 활용가능한 class를 정의할 수 있다. __enter__ 는 런타임 진입 시 동작을 정의할 수 있으며, __exit__ 는 런타임 종료 시 동작을 정의할 수 있다. 또한 __enter__ 의 반환값은 with 구문 내에서 alias를 사용할 때 해당 alias 변수가 가지는 object 가 된다.

파일쓰기를 실행할 수 있는 context manager 객체를, class를 활용하여 정의한 예시이다.

# Use Class -> Context Manager with exception handling

class MyFileWriter():
    def __init__(self, file_name, method):
        print('MyFileWriter started : __init__')
        self.file_obj = open(file_name, method)
        
    def __enter__(self):
        print('MyFileWriter started : __enter__')
        return self.file_obj

    def __exit__(self, exc_type, value, trace_back):
        print('MyFileWriter started : __exit__')
        if exc_type:
            print("Logging exception {}".format((exc_type, value, trace_back)))
        self.file_obj.close()
        

with MyFileWriter('testfile3.txt', 'w') as f:
    f.write('Context Manager Test3.\nContextlib Test3.')

 

시간측정을 실행할 수 있는 context manager 객체를, class를 활용하여 정의한 예시이다.

# Use Class

import time
class ExcuteTimerCls(object):
    def __init__(self, msg):
        self._msg = msg

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

    def __exit__(self, exc_type, exc_value, exc_traceback):
        if exc_type:
            print("Logging exception {}".format((exc_type, exc_value, exc_traceback)))
        else:
            print('{}: {} s'.format(self._msg, time.monotonic() - self._start))
        return True # True

with ExcuteTimerCls("Start! job") as v:
    print('Received start monotonic1 : {}'.format(v))
    # Excute job.
    for i in range(10000000):
        pass
    # raise Exception("Raise! Exception.") # 강제로 발생

 

Contextlib 데코레이터 사용하여 더욱 간결하게 context manager 객체를 함수로 정의할 수 있다. 코드가 직관적이며, 예외처리 또한 용이하다.

# Use decorator 

import contextlib
import time

@contextlib.contextmanager
def ExcuteTimerDc(msg):
    start = time.monotonic()
    try: #__enter__
        yield start
    except BaseException as e:
        print('Logging exception: {}: {}'.format(msg, e))
        raise
    else: #__exit__
        print('{}: {} s'.format(msg, time.monotonic() - start))


with ExcuteTimerDc("Start! job") as v:
    print('Received start monotonic2 : {}'.format(v))
    # Excute job.
    for i in range(10000000):
        pass
    # raise ValueError('occurred.')

관련글 더보기

댓글 영역