ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Context Manager 섹시하게 사용하기 😎
    💻 프로그래밍/Python 2022. 1. 22. 16:17

    안녕하세요. 오늘은 Context Manager에 대해서 알아보려고 합니다.

    먼저 Context Manager가 뭔지에 대해 알아야겠죠?

    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 (described in section The with statement), but can also be used by directly invoking their methods.

     

    특정 로직을 실행할 때 일정한 런타임 환경을 만들어 주기 위해 진입과 종료를 핸들링 하는게 context manager입니다. 

    다들 한 번쯤 본 적 있는 with 문이 있을 겁니다.

     

    1
    2
    with open('file''w'as opened_file:
        opened_file.write('test')
    cs

     

    저는 처음에 with 문을 쓰면 close까지 알아서 해주는 그런 기능으로만 알고 있었는데, 이게 바로 context manager 였네요 ㅎㅎ

    이렇게 file open 같은 로직들은 꼭 close 까지 안전하게 해줘야 하죠. 그렇기 때문에 시작과 끝가지 안전하게 핸들링을 위해 context manager를 사용합니다.

     

    그럼 이런 context manager를 직접 만들고 응용하는 방법을 알아보겠습니다🎉

     

    💎 class 형태


    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    class ExceptionManager:
     
        def __enter__(self):
            pass
     
        def __exit__(self, exc_type, exc_val, exc_tb):
            pass
     
     
    with ExceptionManager() as em:
        print('test')
     
    cs

    context manager__enter__(), __exit__() 두 개의 매직 메서드를 기본적으로 가지고 있어야 합니다. 함수명 그대로 들어오고 나가는 부분을 정의하는 함수들입니다.

     

    __exit__()에 전달되는 파라미터들은 예외처리 관련된 파라미터들인데 아래와 같이 정의합니다.

    exc_type: Exception 타입
    exc_val: Exception 값
    exc_tb: Exception Traceback

     

    Exception이 없는 경우 위 세 가지 값은 모두 None입니다.

     

    print 로 확인 해본 실행 순서

    위 이미지처럼 context manager를 사용한 결과를 확인할 수 있습니다.

    제가 최근에 context manager를 회사 로직에서 유용하게 사용할 수 있었던 건 예외처리에 대한 부분입니다. 보통 핸들링하고 싶은 오류 등이 있을 때 try-except 구문을 사용해서 slack 알림을 보내는 로직을 썼습니다.

     

    예외처리 예시

    이런 반복적인 예외처리 로직이 여러 함수에 덕지덕지 있으니까 보기가 안 좋았습니다. 사실 그전에 데코레이터(decorator)를 사용해 반복적인 작업을 처리하긴 했습니다.

     

    context manager를 사용한 이유는

    한 함수 내에서 다르게 예외처리를 해야 하거나 하는 경우에 데코레이터가 아닌
    context manager를 사용하면 좋지 않을까?

     

    하는 팀장님의 제안 때문이었습니다. 덕분에 context manager에 대해 이렇게 글까지 쓰게 되었네요 ㅋㅋ (감사합니다 팀장님🙏)

     

    여하튼 본론으로 가서, 저런 예외처리를 context manager를 통해 처리해보겠습니다.

     

    context manager를 사용해서 예외처리한 예

    보시면 division by zero 오류가 난 이후에도 다음 동작이 잘 실행된 걸 확인할 수 있습니다. __exit__() 함수가 True를 return 하면 오류 없이 실행이 됩니다.

     

    이제 더 이상 동일한 예외처리를 위해 try-except를 덕지덕지 추가할 필요가 없습니다!!

     

    📚 contextlib 사용


    두 번째 방법은 contextlib.contextmanger 데코레이터를 사용하는 방법입니다. python에서는 편리하게 미리 라이브러리에 정의되어 있네요. 다만 contextlib.contextmanger를 사용하기 위해서는 내부에 yeild를 사용하여 generator iterator 객체를 반환해야 합니다. 쉽게 말해 제너레이터(generator) 형태로 함수를 만들면 된다는 겁니다. (그 이유는 좀 더 뒤에 설명)

     

    예시로 보겠습니다!

    contextmanger 데코레이터를 사용한 예시

    exception_manager라는 함수를 정의했고, @contextmanger를 데코레이터로 사용했습니다. class로 선언한 context manager와 동일하게 처리된 모습을 볼 수 있습니다.

     

    특이한 점은 위 설명에서도 말했다시피 yeild를 사용한다는 점입니다. 왜일까요?! (사실 그냥 그런가 보다 했는데 글 쓰다가 궁금해서 찾아봄 ㅋㅋ)

     

    @contextmanger 내부 로직

    @contextmanger 내부 로직을 보면 _GeneratorContextManager를 리턴하고 있습니다. _GeneratorContextManager 함수에 들어가 보니 우리가 앞에서 구현했던 context_manger 형태의 class로 구현되어 있습니다.

     

    __enter__(), __exit__() 보면 next() 함수로 self.gen을 실행하고 있고, self.gen은 전달된 function입니다. 여기서 next()가 generator이기 때문에 yeild를 사용하는 것입니다.

     

    추가로 비동기 로직에서는 @asynccontextmanager 를 사용합니다!

     

    👋 정리 


    coontext manager를 class 형태로 만드는 방법과 contextlib를 사용하여 함수 형태로 구현하는 방법을 알아봤습니다.

    중요한 건 런타임 시 시작과 끝 일정하게 처리하기 위한 로직이 있을 때 context manager를 사용하면 된다는 것입니다.

     

    하지만 꼭 context manager가 아니라 데코레이터를 사용해서 처리할 수 있습니다. 상황에 맞게 잘 선택하여 사용하시면 될 것 같습니다. 그럼 오늘도 좋은 하루 되시고 즐거운 코딩 하세요!

     

    댓글

운동하는 개발자 JAY-JI