Python의 컨텍스트 관리자.

이 게시물은 Pradhavan 님의 강연에서 영감을 받았습니다.

컨텍스트 관리자란 무엇입니까?



다음은 Python의 공식 문서에 나와 있는 내용입니다.
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. – Python Docs


그러나 그것은 모든 기반을 덮고 있을 뿐입니다. 단순화 된 버전을 이해합시다.

프로그래머는 때때로 파일, 데이터베이스 연결, 잠금 등과 같은 외부 리소스를 사용합니다. 컨텍스트 관리자를 사용하면 다음을 지정하여 이러한 리소스를 관리할 수 있습니다.
  • 리소스를 획득했을 때 해야 할 일과
  • 리소스가 해제될 때 수행할 작업

  • 컨텍스트 관리자가 필요한 이유는 무엇입니까?



    다음 예를 고려하십시오.

    for _ in range(100000):
        file = open("foo.txt", "w")
        files.append(f)
        file.close()
    


    파일 설명자가 매번 해제되도록 하기 위해 close() 메서드를 호출하고 있음을 주목하세요. 그렇게 하지 않으면 OS는 결국 파일 설명자를 열 수 있는 허용 한도를 다 써버릴 것입니다.

    그러나 컨텍스트 관리자를 사용하여 위 코드의 보다 파이썬적인 버전을 작성합니다.

    for _ in range(100000):
        with open("foo.txt", "r") as f:
            files.append(f)
    


    여기 open("foo.txt", "r")with 문을 사용하여 활성화되는 컨텍스트 관리자입니다. 명시적으로 파일을 닫을 필요가 없었고 컨텍스트 관리자가 처리해 주었습니다. 마찬가지로 Python에는 작업을 더 쉽게 해주는 미리 정의된 다른 컨텍스트 관리자가 있습니다.

    자체 컨텍스트 관리자를 정의할 수 있습니까?



    예. 사용자 정의 컨텍스트 관리자를 정의하는 두 가지 방법이 있습니다.
  • 클래스 기반 정의.
  • 함수 기반 정의.

  • 클래스 기반 컨텍스트 관리자



    파일 예제를 계속 진행하고 open() 에뮬레이트할 컨텍스트 관리자를 정의해 보겠습니다.

    files = []
    
    class Open():
        def __init__(self, filename, mode):
            self.filename = filename
            self.mode = mode
    
        def __enter__(self):
            self.open_file = open(self.filename, self.mode)
            print("Enter called")
            return self.open_file
    
        def __exit__(self, *args):
            print("Exit Called")
            self.open_file.close()
    
    for _ in range(100000):
        with Open('foo.txt', 'w') as f:
            files.append(f)
    


  • __enter__ 메서드는 리소스를 획득할 때 수행할 작업, 즉 파일 개체를 제공하는 방법을 알려줍니다.
  • __exit__ 메서드는 컨텍스트 관리자를 종료할 때(즉, 파일을 닫을 때) 수행할 작업을 정의합니다.
  • 모든 루프에서 __enter____exit__가 어떻게 호출되는지 볼 수 있습니다.

  • 오류 처리



    파이썬FileNotFoundError으로 open()를 어떻게 처리합니까?

    try:
        with open("foo.txt", "r") as f:
            content = f.readlines()
    except FileNotFoundError as e:
        print("Hey, file isn't there. Let's log it.")
    


    파일을 열 때마다 필요한 기본 오류 처리 코드입니다. 사용자 정의 컨텍스트 관리자로 DRY를 시도해 보겠습니다.

    class Open():
        def __init__(self, filename, mode):
            self.filename = filename
            self.mode = mode
    
        def __enter__(self):
            print("Enter called")
    
            try:
                self.open_file = open(self.filename, self.mode)
                return self.open_file
            except FileNotFoundError as e:
                print("Hey, file isn't there. Let's log it.")
    
        def __exit__(self, exc_type, exc_value, exc_traceback): #notice the parameters
            print("Exit Called")
            if(exc_type is None):
                self.open_file.close()
                return True
            else:
                return True
    
    with Open("foo.txt", "r") as f:
        content = f.readlines()
    

    __exit__의 변경 사항
  • exc_type__enter__에서 오류를 처리하는 동안 얻을 수 있는 오류 클래스 유형입니다(이 경우 AttributeError).
  • exc_value__enter__ 에서 오류를 처리하는 동안 얻게 될 오류 값입니다.
  • exc_traceback__enter__ 에서 오류를 처리하는 동안 얻게 될 오류의 역추적입니다.
  • 오류 역추적을 억제하기 위해 True를 반환합니다(exc_traceback 매개변수와 혼동하지 마십시오).

  • 또 다른 실제 사례




    class DatabaseHandler():
        def __init__(self):
            self.host = '127.0.0.1'
            self.user = 'dev'
            self.password = 'dev@123'
            self.db = 'foobar'
            self.port = '5432'
            self.connection = None
            self.cursor = None
    
        def __enter__(self):
            self.connection = psycopg2.connect(
                user=self.user,
                password=self.password,
                host=self.host,
                port=self.port,
                database=self.db
            )
            self.cursor = self.connection.cursor()
            return self.cursor
    
        def __exit__(self, *args):
            self.cursor.close()
            self.connection.close()
    


    기능 기반 컨텍스트 관리자



    함수 기반 컨텍스트 관리는 contextlib 라는 라이브러리를 사용하여 수행되며 이를 통해 간단한 생성기 함수를 컨텍스트 관리자로 변경할 수 있습니다. 일반적인 청사진은 다음과 같습니다.

    from contextlib import contextmanager
    
    @contextmanager
    def foobar():
        print("What you would typically put in __enter__")
        yield {}
        print("What you would typically put in __exit__")
    
    with foobar() as f:
        print(f)
    


  • contextmanager 데코레이터는 모든 생성기 기능을 컨텍스트 관리자로 전환하는 데 사용됩니다.
  • yield는 컨텍스트 관리자의 __enter__ 부분과 __exit__ 부분 사이에서 구분자로 작동합니다.

  • 파일 처리

    from contextlib import contextmanager
    
    @contextmanager
    def open_(filename, mode):
    
        print("SETUP")
        open_file = open(filename, mode)
    
        try:
            print("EXECUTION")
            yield open_file
    
        except:
            print("Hey, file isn't there. Let's log it.")
    
        finally:
            print("CLEAN-UP")
            open_file.close()
    
    
    with open_("somethign.txt", "w") as f: #notice the mode
        content = f.readlines() #you cannot read on write mode
    


    사용자가 파일 객체로 무엇을 할 지 모르기 때문에 yieldtry 블록으로 래핑합니다. 의도하지 않은 방식으로 사용하려고 할 수 있습니다(위 참조).

    데이터베이스 연결

    from contextlib import contextmanager
    
    @contextmanager
    def database_handler():
        try:
            host = '127.0.0.1'
            user = 'dev'
            password = 'dev@123'
            db = 'foobar'
            port = '5432'
            connection = psycopg2.connect(
                user=user,
                password=password,
                host=host,
                port=port,
                database=db
            )
            cursor = connection.cursor()
            yield cursor
    
        except:
            print("Hey, file isn't there. Let's log it.")
    
        finally:
            cursor.close()
            connection.close()
    



    자원



    우리는 컨텍스트 관리자에 대한 소개만 다루었지만 이는 빙산의 일각에 불과하며 흥미로운 사용 사례가 많이 있습니다. 내가 찾은 몇 가지 흥미로운 링크는 다음과 같습니다.
  • https://docs.python.org/3/reference/datamodel.html#context-managers
  • https://docs.python.org/3/library/contextlib.html
  • https://rednafi.github.io/digressions/python/2020/03/26/python-contextmanager.html
  • https://www.markus-gattol.name/ws/python.html#context_manager
  • 좋은 웹페이지 즐겨찾기