새로운 기본 타입 만들기

Beginner

This tutorial is from open-source community. Access the source code

소개

이 랩에서는 Python 에서 새로운 기본형 (primitive type) 을 생성하고, 이에 필요한 필수 메서드를 구현하는 방법을 배우게 됩니다. 또한 Python 의 객체 프로토콜에 대한 이해를 얻게 될 것입니다. 대부분의 Python 프로그램에서 int, float, str과 같은 내장 기본형은 데이터를 나타내는 데 사용됩니다. 하지만 Python 은 표준 라이브러리의 decimalfractions 모듈에서 볼 수 있듯이, 사용자 정의 형식을 생성할 수 있도록 합니다.

이 랩에서는 MutInt (Mutable Integer, 가변 정수) 라는 새로운 기본형을 생성할 것입니다. Python 의 불변 정수와 달리, MutInt는 생성 후 수정될 수 있습니다. 이 연습은 Python 에서 완벽하게 기능하는 기본형을 생성하는 데 필요한 기본적인 원리를 보여줄 것입니다.

이것은 가이드 실험입니다. 학습과 실습을 돕기 위한 단계별 지침을 제공합니다.각 단계를 완료하고 실무 경험을 쌓기 위해 지침을 주의 깊게 따르세요. 과거 데이터에 따르면, 이것은 초급 레벨의 실험이며 완료율은 90%입니다.학습자들로부터 97%의 긍정적인 리뷰율을 받았습니다.

기본 MutInt 클래스 생성

가변 정수형 (Mutable Integer type) 에 대한 기본 클래스를 생성하는 것으로 시작해 보겠습니다. 프로그래밍에서 클래스는 객체를 생성하기 위한 청사진과 같습니다. 이 단계에서는 새로운 기본형의 기초를 만들 것입니다. 기본형은 프로그래밍 언어에서 제공하는 기본적인 데이터 유형이며, 여기서는 사용자 정의 기본형을 직접 만들 것입니다.

  1. WebIDE 를 열고 /home/labex/project 디렉토리로 이동합니다. WebIDE 는 코드를 작성, 편집 및 실행할 수 있는 통합 개발 환경입니다. 이 디렉토리로 이동하면 모든 파일이 한 곳에 정리되고 서로 제대로 상호 작용할 수 있습니다.

  2. 설정 단계에서 생성된 mutint.py 파일을 엽니다. 이 파일은 MutInt 클래스 정의의 중심이 될 것입니다.

  3. 기본 MutInt 클래스를 정의하기 위해 다음 코드를 추가합니다.

## mutint.py

class MutInt:
    """
    생성 후 값을 수정할 수 있는 가변 정수 클래스입니다.
    """
    __slots__ = ['value']

    def __init__(self, value):
        """정수 값으로 초기화합니다."""
        self.value = value

__slots__ 속성은 이 클래스가 가질 수 있는 속성을 정의하는 데 사용됩니다. 속성은 클래스의 객체에 속하는 변수와 같습니다. __slots__를 사용하면 Python 에게 속성을 저장하는 데 더 메모리 효율적인 방법을 사용하도록 지시합니다. 이 경우, MutInt 클래스는 value라는 단일 속성만 갖게 됩니다. 즉, MutInt 클래스의 각 객체는 정수 값인 하나의 데이터 조각만 저장할 수 있습니다.

__init__ 메서드는 클래스의 생성자입니다. 생성자는 클래스의 객체가 생성될 때 호출되는 특수한 메서드입니다. 값 매개변수를 받아 인스턴스의 value 속성에 저장합니다. 인스턴스는 클래스 청사진에서 생성된 개별 객체입니다.

클래스를 테스트하기 위해 Python 스크립트를 생성하여 사용해 보겠습니다.

  1. 동일한 디렉토리에 test_mutint.py라는 새 파일을 생성합니다.
## test_mutint.py

from mutint import MutInt

## MutInt 객체 생성
a = MutInt(3)
print(f"Created MutInt with value: {a.value}")

## 값 수정 (가변성 시연)
a.value = 42
print(f"Modified value to: {a.value}")

## 더하기 시도 (실패할 것입니다)
try:
    result = a + 10
    print(f"Result of a + 10: {result}")
except TypeError as e:
    print(f"Error when adding: {e}")

이 테스트 스크립트에서는 먼저 mutint.py 파일에서 MutInt 클래스를 가져옵니다. 그런 다음 초기 값이 3 인 MutInt 클래스의 객체를 생성합니다. 초기 값을 출력한 다음 42 로 수정하고 새 값을 출력합니다. 마지막으로, MutInt 객체에 10 을 더하려고 시도합니다. 이는 클래스가 아직 더하기 연산을 지원하지 않기 때문에 오류가 발생합니다.

  1. 터미널에서 다음 명령을 실행하여 테스트 스크립트를 실행합니다.
python3 /home/labex/project/test_mutint.py

터미널은 시스템 및 코드와 상호 작용하기 위해 다양한 명령을 실행할 수 있는 명령줄 인터페이스입니다. 이 명령을 실행하면 test_mutint.py 스크립트가 실행됩니다.

다음과 유사한 출력을 볼 수 있습니다.

Created MutInt with value: 3
Modified value to: 42
Error when adding: unsupported operand type(s) for +: 'MutInt' and 'int'

MutInt 클래스는 값을 성공적으로 저장하고 업데이트합니다. 그러나 몇 가지 제한 사항이 있습니다.

  • 인쇄 시 제대로 표시되지 않습니다.
  • 덧셈과 같은 수학 연산을 지원하지 않습니다.
  • 비교를 지원하지 않습니다.
  • 타입 변환을 지원하지 않습니다.

다음 단계에서는 MutInt 클래스가 진정한 기본형처럼 동작하도록 이러한 제한 사항을 하나씩 해결할 것입니다.

문자열 표현 개선

Python 에서 MutInt 객체를 인쇄하면 <__main__.MutInt object at 0x...>와 같은 출력을 보게 됩니다. 이 출력은 MutInt 객체의 실제 값을 알려주지 않으므로 그다지 유용하지 않습니다. 객체가 나타내는 것을 더 쉽게 이해하기 위해 문자열 표현을 위한 특수 메서드를 구현할 것입니다.

  1. WebIDE 에서 mutint.py를 열고 다음 코드로 업데이트합니다.
## mutint.py

class MutInt:
    """
    생성 후 값을 수정할 수 있는 가변 정수 클래스입니다.
    """
    __slots__ = ['value']

    def __init__(self, value):
        """정수 값으로 초기화합니다."""
        self.value = value

    def __str__(self):
        """인쇄를 위한 문자열 표현을 반환합니다."""
        return str(self.value)

    def __repr__(self):
        """개발자 친화적인 문자열 표현을 반환합니다."""
        return f'MutInt({self.value!r})'

    def __format__(self, fmt):
        """형식 사양을 사용하여 문자열 형식을 지원합니다."""
        return format(self.value, fmt)

MutInt 클래스에 세 가지 중요한 메서드를 추가했습니다.

  • __str__(): 이 메서드는 객체에 str() 함수를 사용하거나 객체를 직접 인쇄할 때 호출됩니다. 사람이 읽을 수 있는 문자열을 반환해야 합니다.
  • __repr__(): 이 메서드는 객체의 "공식적인" 문자열 표현을 제공합니다. 주로 디버깅에 사용되며, 이상적으로는 eval() 함수에 전달하면 객체를 다시 생성하는 문자열을 반환해야 합니다.
  • __format__(): 이 메서드를 사용하면 MutInt 객체와 함께 Python 의 문자열 형식 시스템을 사용할 수 있습니다. 패딩 및 숫자 형식 지정과 같은 형식 사양을 사용할 수 있습니다.
  1. 이러한 새 메서드를 테스트하기 위해 test_string_repr.py라는 새 테스트 파일을 생성합니다.
## test_string_repr.py

from mutint import MutInt

## MutInt 객체 생성
a = MutInt(3)

## 문자열 표현 테스트
print(f"str(a): {str(a)}")
print(f"repr(a): {repr(a)}")

## 직접 인쇄 테스트
print(f"Print a: {a}")

## 문자열 형식 지정 테스트
print(f"Formatted with padding: '{a:*^10}'")
print(f"Formatted as decimal: '{a:d}'")

## 가변성 테스트
a.value = 42
print(f"After changing value, repr(a): {repr(a)}")

이 테스트 파일에서는 먼저 MutInt 클래스를 가져옵니다. 그런 다음 값 3으로 MutInt 객체를 생성합니다. str()repr() 함수를 사용하여 __str__()__repr__() 메서드를 테스트합니다. 또한 직접 인쇄, 문자열 형식 지정 및 MutInt 객체의 가변성을 테스트합니다.

  1. 테스트 스크립트를 실행합니다.
python3 /home/labex/project/test_string_repr.py

이 명령을 실행하면 Python 은 test_string_repr.py 스크립트를 실행합니다. 다음과 유사한 출력을 볼 수 있습니다.

str(a): 3
repr(a): MutInt(3)
Print a: 3
Formatted with padding: '****3*****'
Formatted as decimal: '3'
After changing value, repr(a): MutInt(42)

이제 MutInt 객체가 보기 좋게 표시됩니다. 문자열 표현은 기본 값을 보여주며, 일반 정수와 마찬가지로 문자열 형식을 사용할 수 있습니다.

__str__()__repr__()의 차이점은 __str__()은 사람이 이해하기 쉬운 출력을 생성하기 위한 것이고, __repr__()은 이상적으로 eval()에 전달하면 객체를 다시 생성하는 문자열을 생성해야 한다는 것입니다. 이것이 __repr__() 메서드에 클래스 이름을 포함시킨 이유입니다.

__format__() 메서드를 사용하면 객체가 Python 의 형식 시스템과 함께 작동하므로 패딩 및 숫자 형식 지정과 같은 형식 사양을 사용할 수 있습니다.

수학 연산 추가

현재 MutInt 클래스는 덧셈과 같은 수학 연산을 지원하지 않습니다. Python 에서 사용자 정의 클래스에 대해 이러한 연산을 활성화하려면 특수 메서드를 구현해야 합니다. 이러한 특수 메서드는 이중 밑줄로 둘러싸여 있기 때문에 "매직 메서드" 또는 "던더 메서드 (dunder methods)"라고도 합니다. 산술 연산에 대한 관련 특수 메서드를 구현하여 덧셈 기능을 추가해 보겠습니다.

  1. WebIDE 에서 mutint.py를 열고 다음 코드로 업데이트합니다.
## mutint.py

class MutInt:
    """
    생성 후 값을 수정할 수 있는 가변 정수 클래스입니다.
    """
    __slots__ = ['value']

    def __init__(self, value):
        """정수 값으로 초기화합니다."""
        self.value = value

    def __str__(self):
        """인쇄를 위한 문자열 표현을 반환합니다."""
        return str(self.value)

    def __repr__(self):
        """개발자 친화적인 문자열 표현을 반환합니다."""
        return f'MutInt({self.value!r})'

    def __format__(self, fmt):
        """형식 사양을 사용하여 문자열 형식을 지원합니다."""
        return format(self.value, fmt)

    def __add__(self, other):
        """덧셈 처리: self + other."""
        if isinstance(other, MutInt):
            return MutInt(self.value + other.value)
        elif isinstance(other, int):
            return MutInt(self.value + other)
        else:
            return NotImplemented

    def __radd__(self, other):
        """역순 덧셈 처리: other + self."""
        ## +와 같은 교환 연산의 경우 __add__를 재사용할 수 있습니다.
        return self.__add__(other)

    def __iadd__(self, other):
        """제자리 덧셈 처리: self += other."""
        if isinstance(other, MutInt):
            self.value += other.value
            return self
        elif isinstance(other, int):
            self.value += other
            return self
        else:
            return NotImplemented

MutInt 클래스에 세 가지 새로운 메서드를 추가했습니다.

  • __add__(): 이 메서드는 + 연산자가 MutInt 객체의 왼쪽에 사용될 때 호출됩니다. 이 메서드 내부에서 먼저 other 피연산자가 MutInt 또는 int의 인스턴스인지 확인합니다. 그렇다면 덧셈을 수행하고 결과로 새 MutInt 객체를 반환합니다. other 피연산자가 다른 것이면 NotImplemented를 반환합니다. 이렇게 하면 Python 이 다른 메서드를 시도하거나 TypeError를 발생시킵니다.
  • __radd__(): 이 메서드는 + 연산자가 MutInt 객체의 오른쪽에 사용될 때 호출됩니다. 덧셈은 교환 연산 (즉, a + bb + a와 동일) 이므로 __add__ 메서드를 간단히 재사용할 수 있습니다.
  • __iadd__(): 이 메서드는 += 연산자가 MutInt 객체에 사용될 때 호출됩니다. 새 객체를 생성하는 대신 기존 MutInt 객체를 수정하고 반환합니다.
  1. 이러한 새 메서드를 테스트하기 위해 test_math_ops.py라는 새 테스트 파일을 생성합니다.
## test_math_ops.py

from mutint import MutInt

## MutInt 객체 생성
a = MutInt(3)
b = MutInt(5)

## 일반 덧셈 테스트
c = a + b
print(f"a + b = {c}")

## int 를 사용한 덧셈 테스트
d = a + 10
print(f"a + 10 = {d}")

## 역순 덧셈 테스트
e = 7 + a
print(f"7 + a = {e}")

## 제자리 덧셈 테스트
print(f"Before a += 5: a = {a}")
a += 5
print(f"After a += 5: a = {a}")

## 참조 공유를 사용한 제자리 덧셈 테스트
f = a  ## f 와 a 는 동일한 객체를 가리킵니다.
print(f"Before a += 10: a = {a}, f = {f}")
a += 10
print(f"After a += 10: a = {a}, f = {f}")

## 지원되지 않는 연산 테스트
try:
    result = a + 3.5  ## float 를 더하는 것은 지원되지 않습니다.
    print(f"a + 3.5 = {result}")
except TypeError as e:
    print(f"Error when adding float: {e}")

이 테스트 파일에서는 먼저 MutInt 클래스를 가져옵니다. 그런 다음 몇 개의 MutInt 객체를 생성하고 다양한 유형의 덧셈 연산을 수행합니다. 또한 제자리 덧셈과 지원되지 않는 연산 (float 추가) 을 시도하는 경우를 테스트합니다.

  1. 테스트 스크립트를 실행합니다.
python3 /home/labex/project/test_math_ops.py

다음과 유사한 출력을 볼 수 있습니다.

a + b = MutInt(8)
a + 10 = MutInt(13)
7 + a = MutInt(10)
Before a += 5: a = MutInt(3)
After a += 5: a = MutInt(8)
Before a += 10: a = MutInt(8), f = MutInt(8)
After a += 10: a = MutInt(18), f = MutInt(18)
Error when adding float: unsupported operand type(s) for +: 'MutInt' and 'float'

이제 MutInt 클래스는 기본 덧셈 연산을 지원합니다. +=를 사용했을 때 af가 모두 업데이트되었음을 확인하십시오. 이는 a += 10이 새 객체를 생성하는 대신 기존 객체를 수정했음을 보여줍니다.

가변 객체 (mutable objects) 를 사용한 이 동작은 Python 의 내장 가변 유형 (mutable types) 인 리스트와 유사합니다. 예를 들어:

a = [1, 2, 3]
b = a
a += [4, 5]  ## a 와 b 가 모두 업데이트됩니다.

반대로, 튜플과 같은 불변 유형 (immutable types) 의 경우 +=는 새 객체를 생성합니다.

c = (1, 2, 3)
d = c
c += (4, 5)  ## c 는 새 객체이고, d 는 여전히 이전 객체를 가리킵니다.

비교 연산 구현

현재 MutInt 객체는 서로 또는 일반 정수와 비교할 수 없습니다. Python 에서 ==, <, <=, >, >=와 같은 비교 연산은 객체로 작업할 때 매우 유용합니다. 이를 통해 서로 다른 객체 간의 관계를 결정할 수 있으며, 이는 정렬, 필터링 및 조건문과 같은 많은 프로그래밍 시나리오에서 중요합니다. 따라서 비교 연산에 대한 특수 메서드를 구현하여 MutInt 클래스에 비교 기능을 추가해 보겠습니다.

  1. WebIDE 에서 mutint.py를 열고 다음 코드로 업데이트합니다.
## mutint.py

from functools import total_ordering

@total_ordering
class MutInt:
    """
    생성 후 값을 수정할 수 있는 가변 정수 클래스입니다.
    """
    __slots__ = ['value']

    def __init__(self, value):
        """정수 값으로 초기화합니다."""
        self.value = value

    def __str__(self):
        """인쇄를 위한 문자열 표현을 반환합니다."""
        return str(self.value)

    def __repr__(self):
        """개발자 친화적인 문자열 표현을 반환합니다."""
        return f'MutInt({self.value!r})'

    def __format__(self, fmt):
        """형식 사양을 사용하여 문자열 형식을 지원합니다."""
        return format(self.value, fmt)

    def __add__(self, other):
        """덧셈 처리: self + other."""
        if isinstance(other, MutInt):
            return MutInt(self.value + other.value)
        elif isinstance(other, int):
            return MutInt(self.value + other)
        else:
            return NotImplemented

    def __radd__(self, other):
        """역순 덧셈 처리: other + self."""
        return self.__add__(other)

    def __iadd__(self, other):
        """제자리 덧셈 처리: self += other."""
        if isinstance(other, MutInt):
            self.value += other.value
            return self
        elif isinstance(other, int):
            self.value += other
            return self
        else:
            return NotImplemented

    def __eq__(self, other):
        """동등성 비교 처리: self == other."""
        if isinstance(other, MutInt):
            return self.value == other.value
        elif isinstance(other, int):
            return self.value == other
        else:
            return NotImplemented

    def __lt__(self, other):
        """작음 비교 처리: self < other."""
        if isinstance(other, MutInt):
            return self.value < other.value
        elif isinstance(other, int):
            return self.value < other
        else:
            return NotImplemented

몇 가지 주요 개선 사항을 추가했습니다.

  1. functools 모듈에서 @total_ordering 데코레이터를 가져와 사용합니다. @total_ordering 데코레이터는 Python 에서 강력한 도구입니다. 클래스에 대한 비교 메서드를 구현할 때 많은 시간과 노력을 절약하는 데 도움이 됩니다. 여섯 개의 모든 비교 메서드 (__eq__, __ne__, __lt__, __le__, __gt__, __ge__) 를 수동으로 정의하는 대신 __eq__와 다른 비교 메서드 (이 경우 __lt__) 하나만 정의하면 됩니다. 그러면 데코레이터가 나머지 네 개의 비교 메서드를 자동으로 생성합니다.

  2. 동등성 비교 (==) 를 처리하기 위해 __eq__() 메서드를 추가합니다. 이 메서드는 두 개의 MutInt 객체 또는 MutInt 객체와 정수가 동일한 값을 갖는지 확인하는 데 사용됩니다.

  3. 작음 비교 (<) 를 처리하기 위해 __lt__() 메서드를 추가합니다. 이 메서드는 하나의 MutInt 객체 또는 정수와 비교된 MutInt 객체가 더 작은 값을 갖는지 확인하는 데 사용됩니다.

  4. 이러한 새 메서드를 테스트하기 위해 test_comparisons.py라는 새 테스트 파일을 생성합니다.

## test_comparisons.py

from mutint import MutInt

## MutInt 객체 생성
a = MutInt(3)
b = MutInt(3)
c = MutInt(5)

## 동등성 테스트
print(f"a == b: {a == b}")  ## True 여야 함 (동일한 값)
print(f"a == c: {a == c}")  ## False 여야 함 (다른 값)
print(f"a == 3: {a == 3}")  ## True 여야 함 (int 와 비교)
print(f"a == 5: {a == 5}")  ## False 여야 함 (다른 값)

## 작음 테스트
print(f"a < c: {a < c}")    ## True 여야 함 (3 < 5)
print(f"c < a: {c < a}")    ## False 여야 함 (5 는 < 3 이 아님)
print(f"a < 4: {a < 4}")    ## True 여야 함 (3 < 4)

## 다른 비교 테스트 (@total_ordering 에서 제공)
print(f"a <= b: {a <= b}")  ## True 여야 함 (3 <= 3)
print(f"a > c: {a > c}")    ## False 여야 함 (3 은 > 5 가 아님)
print(f"c >= a: {c >= a}")  ## True 여야 함 (5 >= 3)

## 다른 유형으로 테스트
print(f"a == '3': {a == '3'}")  ## False 여야 함 (다른 유형)

이 테스트 파일에서는 여러 MutInt 객체를 생성하고 이에 대해 다양한 비교 연산을 수행합니다. 또한 MutInt 객체를 일반 정수 및 다른 유형 (이 경우 문자열) 과 비교합니다. 이러한 테스트를 실행하여 비교 메서드가 예상대로 작동하는지 확인할 수 있습니다.

  1. 테스트 스크립트를 실행합니다.
python3 /home/labex/project/test_comparisons.py

다음과 유사한 출력을 볼 수 있습니다.

a == b: True
a == c: False
a == 3: True
a == 5: False
a < c: True
c < a: False
a < 4: True
a <= b: True
a > c: False
c >= a: True
a == '3': False

이제 MutInt 클래스는 모든 비교 연산을 지원합니다.

@total_ordering 데코레이터는 여섯 개의 모든 비교 메서드를 수동으로 구현할 필요가 없으므로 특히 유용합니다. __eq____lt__만 제공하면 Python 이 나머지 네 개의 비교 메서드를 자동으로 파생할 수 있습니다.

사용자 정의 클래스를 구현할 때는 일반적으로 동일한 유형의 객체와 의미가 있는 내장 유형 모두에서 작동하도록 하는 것이 좋습니다. 이것이 비교 메서드가 MutInt 객체와 일반 정수를 모두 처리하는 이유입니다. 이렇게 하면 MutInt 클래스를 다양한 프로그래밍 시나리오에서 더 유연하게 사용할 수 있습니다.

타입 변환 추가

현재 MutInt 클래스는 덧셈 및 비교 연산을 지원합니다. 그러나 int()float()와 같은 Python 의 내장 변환 함수와는 작동하지 않습니다. 이러한 변환 함수는 Python 에서 매우 유용합니다. 예를 들어, 다른 계산 또는 연산을 위해 값을 정수 또는 부동 소수점 숫자로 변환하려는 경우 이러한 함수에 의존합니다. 따라서 MutInt 클래스가 이러한 함수와 함께 작동할 수 있도록 기능을 추가해 보겠습니다.

  1. WebIDE 에서 mutint.py를 열고 다음 코드로 업데이트합니다.
## mutint.py

from functools import total_ordering

@total_ordering
class MutInt:
    """
    생성 후 값을 수정할 수 있는 가변 정수 클래스입니다.
    """
    __slots__ = ['value']

    def __init__(self, value):
        """정수 값으로 초기화합니다."""
        self.value = value

    def __str__(self):
        """인쇄를 위한 문자열 표현을 반환합니다."""
        return str(self.value)

    def __repr__(self):
        """개발자 친화적인 문자열 표현을 반환합니다."""
        return f'MutInt({self.value!r})'

    def __format__(self, fmt):
        """형식 사양을 사용하여 문자열 형식을 지원합니다."""
        return format(self.value, fmt)

    def __add__(self, other):
        """덧셈 처리: self + other."""
        if isinstance(other, MutInt):
            return MutInt(self.value + other.value)
        elif isinstance(other, int):
            return MutInt(self.value + other)
        else:
            return NotImplemented

    def __radd__(self, other):
        """역순 덧셈 처리: other + self."""
        return self.__add__(other)

    def __iadd__(self, other):
        """제자리 덧셈 처리: self += other."""
        if isinstance(other, MutInt):
            self.value += other.value
            return self
        elif isinstance(other, int):
            self.value += other
            return self
        else:
            return NotImplemented

    def __eq__(self, other):
        """동등성 비교 처리: self == other."""
        if isinstance(other, MutInt):
            return self.value == other.value
        elif isinstance(other, int):
            return self.value == other
        else:
            return NotImplemented

    def __lt__(self, other):
        """작음 비교 처리: self < other."""
        if isinstance(other, MutInt):
            return self.value < other.value
        elif isinstance(other, int):
            return self.value < other
        else:
            return NotImplemented

    def __int__(self):
        """int 로 변환합니다."""
        return self.value

    def __float__(self):
        """float 로 변환합니다."""
        return float(self.value)

    __index__ = __int__  ## 배열 인덱싱 및 인덱스가 필요한 기타 연산 지원

    def __lshift__(self, other):
        """왼쪽 시프트 처리: self << other."""
        if isinstance(other, MutInt):
            return MutInt(self.value << other.value)
        elif isinstance(other, int):
            return MutInt(self.value << other)
        else:
            return NotImplemented

    def __rlshift__(self, other):
        """역순 왼쪽 시프트 처리: other << self."""
        if isinstance(other, int):
            return MutInt(other << self.value)
        else:
            return NotImplemented

MutInt 클래스에 세 가지 새로운 메서드를 추가했습니다.

  1. __int__(): 이 메서드는 MutInt 클래스의 객체에 대해 int() 함수를 사용할 때 호출됩니다. 예를 들어, MutInt 객체 a가 있고 int(a)를 작성하면 Python 은 a 객체의 __int__() 메서드를 호출합니다.
  2. __float__(): 마찬가지로 이 메서드는 MutInt 객체에 대해 float() 함수를 사용할 때 호출됩니다.
  3. __index__(): 이 메서드는 특히 정수 인덱스가 필요한 연산에 사용됩니다. 예를 들어, 인덱스를 사용하여 목록의 요소에 액세스하거나 비트 길이 연산을 수행하려는 경우 Python 은 정수 인덱스가 필요합니다.
  4. __lshift__(): 이 메서드는 MutInt 객체가 << 연산자의 왼쪽에 있을 때 왼쪽 시프트 연산을 처리합니다.
  5. __rlshift__(): 이 메서드는 MutInt 객체가 << 연산자의 오른쪽에 있을 때 왼쪽 시프트 연산을 처리합니다.

__index__ 메서드는 목록 인덱싱, 슬라이싱 및 비트 길이 연산과 같이 정수 인덱스가 필요한 연산에 매우 중요합니다. 간단한 구현에서는 __int__와 동일하게 설정했습니다. 이는 MutInt 객체의 값을 정수 인덱스로 직접 사용할 수 있기 때문입니다.

__lshift____rlshift__ 메서드는 비트 단위 왼쪽 시프트 연산을 지원하는 데 필수적입니다. 이를 통해 MutInt 객체가 비트 단위 연산에 참여할 수 있으며, 이는 정수와 유사한 유형에 대한 일반적인 요구 사항입니다.

  1. 이러한 새 메서드를 테스트하기 위해 test_conversions.py라는 새 테스트 파일을 생성합니다.
## test_conversions.py

from mutint import MutInt

## MutInt 객체 생성
a = MutInt(3)

## 변환 테스트
print(f"int(a): {int(a)}")
print(f"float(a): {float(a)}")

## 인덱스로 사용 테스트
names = ['Dave', 'Guido', 'Paula', 'Thomas', 'Lewis']
print(f"names[a]: {names[a]}")

## 비트 연산에서 사용 테스트 (__index__ 필요)
print(f"1 << a: {1 << a}")  ## 3 만큼 왼쪽으로 시프트

## 16 진수/8 진수/2 진수 함수 테스트 (__index__ 필요)
print(f"hex(a): {hex(a)}")
print(f"oct(a): {oct(a)}")
print(f"bin(a): {bin(a)}")

## 수정하고 다시 테스트
a.value = 4
print(f"\n값을 4 로 변경한 후:")
print(f"int(a): {int(a)}")
print(f"names[a]: {names[a]}")
  1. 테스트 스크립트를 실행합니다.
python3 /home/labex/project/test_conversions.py

다음과 유사한 출력을 볼 수 있습니다.

int(a): 3
float(a): 3.0
names[a]: Thomas
1 << a: 8
hex(a): 0x3
oct(a): 0o3
bin(a): 0b11

값을 4로 변경한 후:
int(a): 4
names[a]: Lewis

이제 MutInt 클래스는 표준 Python 유형으로 변환될 수 있으며 정수 인덱스가 필요한 연산에 사용할 수 있습니다.

__index__ 메서드는 특히 중요합니다. 이 메서드는 목록 인덱싱, 비트 단위 연산 및 hex(), oct(), bin()과 같은 다양한 함수와 같이 정수 인덱스가 필요한 상황에서 객체를 사용할 수 있도록 Python 에 도입되었습니다.

이러한 추가 기능을 통해 MutInt 클래스는 이제 상당히 완전한 기본 유형입니다. 가변이라는 추가적인 이점과 함께 일반 정수가 사용되는 대부분의 컨텍스트에서 사용할 수 있습니다.

완전한 MutInt 구현

다음은 추가한 모든 기능이 포함된 완전한 MutInt 구현입니다.

## mutint.py

from functools import total_ordering

@total_ordering
class MutInt:
    """
    생성 후 값을 수정할 수 있는 가변 정수 클래스입니다.
    """
    __slots__ = ['value']

    def __init__(self, value):
        """정수 값으로 초기화합니다."""
        self.value = value

    def __str__(self):
        """인쇄를 위한 문자열 표현을 반환합니다."""
        return str(self.value)

    def __repr__(self):
        """개발자 친화적인 문자열 표현을 반환합니다."""
        return f'MutInt({self.value!r})'

    def __format__(self, fmt):
        """형식 사양을 사용하여 문자열 형식을 지원합니다."""
        return format(self.value, fmt)

    def __add__(self, other):
        """덧셈 처리: self + other."""
        if isinstance(other, MutInt):
            return MutInt(self.value + other.value)
        elif isinstance(other, int):
            return MutInt(self.value + other)
        else:
            return NotImplemented

    def __radd__(self, other):
        """역순 덧셈 처리: other + self."""
        return self.__add__(other)

    def __iadd__(self, other):
        """제자리 덧셈 처리: self += other."""
        if isinstance(other, MutInt):
            self.value += other.value
            return self
        elif isinstance(other, int):
            self.value += other
            return self
        else:
            return NotImplemented

    def __eq__(self, other):
        """동등성 비교 처리: self == other."""
        if isinstance(other, MutInt):
            return self.value == other.value
        elif isinstance(other, int):
            return self.value == other
        else:
            return NotImplemented

    def __lt__(self, other):
        """작음 비교 처리: self < other."""
        if isinstance(other, MutInt):
            return self.value < other.value
        elif isinstance(other, int):
            return self.value < other
        else:
            return NotImplemented

    def __int__(self):
        """int 로 변환합니다."""
        return self.value

    def __float__(self):
        """float 로 변환합니다."""
        return float(self.value)

    __index__ = __int__  ## 배열 인덱싱 및 인덱스가 필요한 기타 연산 지원

    def __lshift__(self, other):
        """왼쪽 시프트 처리: self << other."""
        if isinstance(other, MutInt):
            return MutInt(self.value << other.value)
        elif isinstance(other, int):
            return MutInt(self.value << other)
        else:
            return NotImplemented

    def __rlshift__(self, other):
        """역순 왼쪽 시프트 처리: other << self."""
        if isinstance(other, int):
            return MutInt(other << self.value)
        else:
            return NotImplemented

이 구현은 Python 에서 새로운 기본 유형을 만드는 주요 측면을 다룹니다. 이를 더욱 완전하게 만들려면 뺄셈, 곱셈, 나눗셈 등과 같은 다른 연산에 대한 추가 메서드를 구현할 수 있습니다.

요약

이 Lab 에서 Python 에서 자체 기본 유형을 만드는 방법을 배웠습니다. 특히, 내장 유형과 유사한 가변 정수 클래스를 만들고, 객체 표시를 위한 특수 메서드를 구현하고, 수학 및 비교 연산에 대한 지원을 추가하고, 다양한 Python 컨텍스트에 대한 타입 변환을 활성화하는 방법을 익혔습니다.

이러한 개념은 Python 의 객체 모델을 이해하는 데 필수적이며, 내장 연산과 잘 통합되는 사용자 정의 유형을 만드는 데 사용할 수 있습니다. 지식을 더욱 넓히려면 더 많은 수학 연산을 구현하고, 다른 내장 함수에 대한 지원을 추가하고, 사용자 정의 컬렉션과 같은 복잡한 유형을 탐색해 보십시오. Python 의 사용자 정의 유형은 특정 요구 사항에 맞게 언어를 확장하는 강력한 도구입니다.