Python의 마법 방법 VII

2024. 4. 13. 15:27python/advanced

객체를 호출 가능하게 만들기

Python에서 콜러블은 한 쌍의 괄호와 선택적으로 일련의 인수를 사용하여 호출할 수 있는 객체입니다. 예를 들어 함수, 클래스 및 메서드는 모두 Python에서 호출 가능합니다. 콜러블은 작업을 수행할 수 있게 해주기 때문에 많은 프로그래밍 언어에서 기본입니다.

Python을 사용하면 자신만의 콜러블을 만들 수 있습니다. 이렇게 하려면 클래스에 .call() 특수 메서드를 추가하면 됩니다. .call() 메소드가 있는 클래스의 모든 인스턴스는 함수처럼 동작합니다.

호출 가능 인스턴스의 일반적인 사용 사례는 호출 간에 계산된 데이터를 캐시하는 상태 저장 호출 가능이 필요한 경우입니다. 이는 일부 알고리즘을 최적화해야 할 때 유용합니다. 예를 들어, 효율성을 위해 이미 계산된 값을 캐시하는 Factorial 클래스를 코딩한다고 가정해 보겠습니다. 이 상황에서는 다음과 같은 작업을 수행할 수 있습니다.

(참고) 아래 코드를 factorial.py 파일로 저장하세요.

In [ ]:
class Factorial:
    def __init__(self):
        self._cache = {0: 1, 1: 1}

    def __call__(self, number):
        if number not in self._cache:
            self._cache[number] = number * self(number - 1)
        return self._cache[number]

이 예에서는 .__call__() 메서드를 사용하여 Factorial를 정의합니다. .__init__() 메서드에서는 이미 계산된 값을 보유하는 ._cache 속성을 생성하고 초기화합니다.

다음으로 .__call__()을 정의합니다. Python은 클래스를 보유한 인스턴스를 함수로 사용할 때 자동으로 이 메서드를 호출합니다. 이 예에서 메서드는 계산된 값을 ._cache에 캐시하는 동안 주어진 숫자의 팩토리얼을 계산합니다.

다음 코드를 실행하여 class를 시도해 보세요.

In [ ]:
from factorial import Factorial

factorial_of = Factorial()

factorial_of(4)
In [ ]:
factorial_of(5)
In [ ]:
factorial_of(6)

이 코드 예제에서는 Factorial의 인스턴스를 만듭니다. 그런 다음 해당 인스턴스를 함수로 사용하여 다양한 숫자의 계승을 계산합니다.

클래스에 .__call__() 매직 메서드를 구현하면 한 쌍의 괄호를 사용하여 호출할 때 함수처럼 동작하는 호출 가능 개체처럼 동작하게 됩니다. 이를 통해 객체 지향 코드에 유연성과 기능을 추가할 수 있습니다.

사용자 정의 시퀀스 및 매핑 구현

Python의 시퀀스와 매핑은 기본적인 내장 데이터 유형입니다. 목록, 튜플, 문자열은 시퀀스의 예이고 사전은 매핑 유형의 예입니다.

필요한 특수 메서드를 구현하여 자신만의 시퀀스형 및 매핑형 클래스를 만들 수 있습니다. 이를 위해 Python은 시퀀스 및 매핑 프로토콜을 정의합니다. 이러한 프로토콜은 특수 메서드의 모음입니다.

예를 들어 시퀀스 프로토콜은 다음 메서드로 구성됩니다.

방법설명
.__getitem__() 다음과 같이 인덱싱을 사용하여 항목에 액세스할 때 호출됩니다.sequence[index]
.__len__() len()기본 시퀀스의 항목 수를 가져오기 위해 내장 함수를 호출할 때 호출됩니다.
.__contains__() inor not in연산자를 사용하여 멤버십 테스트에서 시퀀스를 사용할 때 호출됩니다.
.__reversed__() reversed()기본 시퀀스의 역방향 버전을 얻기 위해 내장 함수를 사용할 때 호출됩니다.

이러한 특수 메서드를 구현하면 Python에서 시퀀스와 유사한 사용자 정의 클래스를 만들 수 있습니다. 친구에게 Stack class를 예를 들어 다시 들려주세요. 멤버십을 지원하는 .__contains__() 메서드를 이미 구현했습니다. 따라서 나머지 세 가지 방법만 필요합니다.

In [ ]:
class Stack:
    # ...

    def __contains__(self, item):
        for current_item in self.items:
            if item == current_item:
                return True
        return False

    # ...

    def __getitem__(self, index):
        return self.items[index]

    def __len__(self):
        return len(self.items)

    def __reversed__(self):
        return type(self)(reversed(self.items))

.__getitem__() 메서드에서는 이미 알고 있듯이 일반적인 list인 내부 데이터 컨테이너에서 대상 항목을 반환합니다. 이 방법을 사용하면 인덱싱 작업을 지원하게 됩니다.

다음으로 내장 len() 함수를 지원하도록 .__len__() 정의합니다. 이 메서드는 내부 데이터 목록의 항목 수를 반환합니다. 마지막으로 .__reversed__()을 정의합니다. 이 방법을 사용하면 내장된 reversed() 함수를 사용하여 원본 데이터의 반전된 버전을 얻을 수 있습니다.

이러한 새로운 기능이 실제로 작동하는 방식은 다음과 같습니다. 업데이트된 클래스를 가져올 수 있도록 대화형 세션을 다시 시작해야 한다는 점을 기억하세요.

In [ ]:
from stack import Stack

stack = Stack([1, 2, 3, 4])

stack[1]
In [ ]:
stack[-1]
In [ ]:
len(stack)
In [ ]:
reversed(stack)

이제 stack 개체가 [index] 연산자와 같이 인덱싱을 지원합니다. 내장 len() 및 reversed()기능도 지원합니다.

Stack 인스턴스와 같이 reversed()를 인수로 호출하면 목록 대신 새 Stack 인스턴스를 얻게 됩니다. 이 동작은 일반적으로 역방향 반복기를 반환하는 reversed()의 기본 동작과 다릅니다. 이 구체적인 예에서는 매직 메서드의 유연성과 강력함을 경험하게 됩니다.

위의 예에서 수행한 것과 유사하게 매핑과 같은 클래스를 직접 만들 수 있습니다. 그렇게 하려면 클래스가 불변인지 가변인지에 따라 collections.abc.Mapping 또는 collections.abc.MutableMapping 추상 기본 클래스에 정의된 특수 메서드를 구현해야 합니다.

컨텍스트 관리자를 사용하여 설정 및 해제 처리

Python의 with 설명은 프로그램에서 외부 리소스를 관리하는 데 매우 유용합니다. 이를 통해 외부 리소스를 사용할 때 컨텍스트 관리자를 활용하여 설정 및 해제 단계를 자동으로 처리할 수 있습니다.

예를 들어 내장 open() 함수는 컨텍스트 관리자이기도 한 파일 객체를 반환합니다. 일반적으로 with 명령문에서 이 함수를 사용하여 작업을 완료할 때 파일 열기 및 닫기를 관리하는 런타임 컨텍스트인 기본 컨텍스트 관리자에 제어권을 넘겨줍니다. 이러한 프로세스는 각각 설정 및 해제로 구성됩니다.

다음은 텍스트 파일 작업에 open()를 사용하는 예입니다.

In [ ]:
with open("hello.txt", mode="w", encoding="utf-8") as file:
    file.write("Hello, World!")
In [ ]:
with open("hello.txt", mode="r", encoding="utf-8") as file:
    print(file.read())

첫 번째 with 문은 쓰기 위해 hello.txt 파일을 엽니다. 들여쓰기된 블록에서 파일에 일부 텍스트를 씁니다. 블록이 실행되면 Python은 hello.txt 파일을 닫아 획득한 리소스를 해제합니다.

두 번째 명령문은 읽을 파일을 열고 해당 내용을 터미널 창에 인쇄합니다.

컨텍스트 관리자를 생성하거나 기존 클래스에 컨텍스트 관리자 기능을 추가하려면 두 가지 특수 메서드를 코딩해야 합니다.

방법설명
.__enter__() 런타임 컨텍스트를 설정하고, 리소스를 획득하고, with 헤더의 지정자를 사용하여 변수에 바인딩할 수 있는 개체를 반환할 수 있습니다.
.__exit__() 런타임 컨텍스트를 정리하고, 리소스를 해제하고, 예외를 처리하고, 컨텍스트에서 발생할 수 있는 예외를 전파할지 여부를 나타내는 부울 값을 반환합니다.

클래스 중 하나에서 컨텍스트 관리자 프로토콜을 지원할 수 있는 방법을 설명하려면 이 섹션 시작 부분의 예제로 돌아가서 읽기 위해 파일을 열 수 있는 컨텍스트 관리자가 필요하다고 말하십시오. 이 상황에서는 다음과 같은 작업을 수행할 수 있습니다.

(참고) 아래 코드를 reader.py 파일로 저장하세요.

In [ ]:
class TextFileReader:
    def __init__(self, file_path, encoding="utf-8"):
        self.file_path = file_path
        self.encoding = encoding

    def __enter__(self):
        self.file_obj = open(self.file_path, mode="r", encoding=self.encoding)
        return self.file_obj

    def __exit__(self, exc_type, exc_val, exc_tb):
        self.file_obj.close()

이 클래스는 파일 경로를 인수로 사용합니다. .__enter__() 메소드는 읽기 위해 파일을 열고 결과 파일 객체를 반환합니다. .__exit__() 메소드는 예외 유형, 값 및 역추적을 나타내는 세 가지 필수 인수를 사용합니다. 그런 다음 해당 개체에 대한 .close() 메서드를 호출하여 파일 개체를 닫습니다.

TextFileReader class가 실제로 진행되는 방식은 다음과 같습니다.

In [ ]:
from reader import TextFileReader

with TextFileReader("hello.txt") as file:
    print(file.read())

클래스는 읽기 모드에서 파일을 열기 위해 open()을 사용할 때 동일하게 동작합니다. TextFileReader 클래스는 .__enter__() 및 .__exit__() 메서드를 지원하도록 만들 때 사용자 정의 클래스에서 활용할 수 있는 기능의 샘플입니다.

결론

이제 Python에서 매직 메소드 또는 던더 메소드라고도 알려진 특수 메소드를 사용하여 클래스를 사용자 정의하는 방법을 알았습니다. 이러한 메서드는 Python의 핵심 객체 지향 기능을 지원하므로 이에 대해 배우는 것은 코드에서 강력한 클래스를 만들고자 하는 Python 프로그래머에게 기본적인 기술입니다.

이 튜토리얼에서는 다음을 배웠습니다.

  • Python의 특별 하거나 마법적인 방법은 무엇 입니까?
  • Python의 매직 메소드 뒤에 숨은 마법은 무엇 인가요?
  • 다양한 클래스의 동작을 사용자 정의하기 위해 특수 메서드를 사용하는 방법

이러한 지식을 바탕으로 이제 코드에서 특별한 메서드의 기능을 활용하여 자신만의 Python 클래스를 심층적으로 사용자 정의할 준비가 되었습니다.

코드 받기: 클래스에서 Python의 매직 메서드를 사용하는 방법을 보여주는 무료 샘플 코드를 다운로드하려면 여기를 클릭하세요. https://realpython.com/bonus/python-magic-methods-code/

'python > advanced' 카테고리의 다른 글

Python의 마법 방법 VI  (0) 2024.04.12
Python의 마법 방법 V  (0) 2024.04.11
Python의 마법 방법 IV  (0) 2024.04.11
Python의 마법 방법 III  (0) 2024.04.09
Python의 마법 방법 II  (0) 2024.04.08