사용자 정의 숫자 매직 메서드

PythonBeginner
지금 연습하기

소개

이 튜토리얼에서는 숫자 연산과 관련된 Python 매직 메서드를 다룹니다. 매직 메서드는 Python 클래스에서 이중 밑줄 (__) 로 시작하고 끝나는 특수한 메서드입니다. "dunder" 메서드 (double underscores) 라고도 합니다.

이러한 매직 메서드를 사용하면 덧셈이나 뺄셈과 같은 특정 연산에서 클래스 인스턴스가 어떻게 동작하는지 정의할 수 있습니다.

다음 섹션을 다룰 것입니다:

  1. 단항 연산자 (Unary Operators)
  2. 이항 연산자 (Binary Operators)
  3. 제자리 연산 (In-place Operations)

시작해 봅시다!

단항 연산자

단항 연산은 부호 반전, 절대값 등과 같이 단일 피연산자를 사용하는 연산입니다.

간단한 객체부터 시작해 보겠습니다. number.py에서 value 속성을 가진 MyNumber라는 클래스를 생성합니다.

class MyNumber:
    def __init__(self, value: float):
        self.value = value

__neg__

__neg__ 매직 메서드는 부호 반전 연산이 어떻게 동작해야 하는지 정의합니다. 클래스 인스턴스에 - 연산자를 사용하면 이 메서드가 호출됩니다.

    ## ... (previous code in number.py)

    def __neg__(self) -> 'MyNumber':
        """Returns the negation of the instance's value."""
        return MyNumber(-self.value)

__abs__

__abs__ 매직 메서드는 절대값 연산이 어떻게 동작해야 하는지 정의합니다. 클래스 인스턴스에 abs() 함수를 사용하면 이 메서드가 호출됩니다.

    ## ... (previous code in number.py)

    def __abs__(self) -> 'MyNumber':
        """Returns the absolute value of the instance's value."""
        return MyNumber(abs(self.value))

__round__

__round__ 매직 메서드는 반올림 연산이 어떻게 동작해야 하는지 정의합니다. 클래스 인스턴스에 round() 함수를 사용하면 이 메서드가 호출됩니다.

    ## ... (previous code in number.py)

    def __round__(self, ndigits: int = None) -> 'MyNumber':
        """Rounds the instance's value to the nearest whole number or specified number of digits."""
        return MyNumber(round(self.value, ndigits))

__floor__

__floor__ 매직 메서드는 바닥 연산이 어떻게 동작해야 하는지 정의합니다. 클래스 인스턴스에 math.floor() 함수를 사용하면 이 메서드가 호출됩니다.

## math module should be import at the top of number.py
import math

    ## ... (previous code in number.py)

    def __floor__(self) -> 'MyNumber':
        """Returns the largest integer less than or equal to the instance's value."""
        return MyNumber(math.floor(self.value))

__ceil__

__ceil__ 매직 메서드는 천장 연산이 어떻게 동작해야 하는지 정의합니다. 클래스 인스턴스에 math.ceil() 함수를 사용하면 이 메서드가 호출됩니다.

    ## ... (previous code in number.py)

    def __ceil__(self) -> 'MyNumber':
        """Returns the smallest integer greater than or equal to the instance's value."""
        return MyNumber(math.ceil(self.value))

예시: 단항 연산자 사용

MyNumber 클래스에 대한 단항 연산자를 정의했으므로, unary_example.py에서 어떻게 작동하는지 살펴보겠습니다.

import math
from number import MyNumber

## Create a new MyNumber object
a = MyNumber(5)
## Use the __neg__ method with the print function
print(f'{a.value=}, {-a.value=}')  ## Output: a.value=5, -a.value=-5

## Create another new MyNumber object
a = MyNumber(-5)
## Use the __abs__ method with the print function
print(f'{a.value=}, {abs(a).value=}')  ## Output: a.value=-5, abs(a).value=5

## Create the third new MyNumber object
a = MyNumber(5.678)
## Use the __round__ method with the print function
print(f'{a.value=}, {round(a, 2).value=}')  ## Output: a.value=5.678, round(a, 2).value=5.68

## Use the __floor__ method with the print function
print(f'{a.value=}, {math.floor(a).value=}')  ## Output: a.value=5.678, math.floor(a).value=5

## Use the __ceil__ method with the print function
print(f'{a.value=}, {math.ceil(a).value=}')  ## Output: a.value=5.678, math.ceil(a).value=6

그런 다음 터미널에서 다음 명령을 입력하여 스크립트를 실행합니다.

python unary_example.py

이항 연산자

이항 연산은 덧셈, 뺄셈, 곱셈, 나눗셈과 같은 산술 연산과 같음, 다름, 작음, 큼 등과 같은 비교 연산과 같이 두 개의 피연산자를 사용하는 연산입니다.

__add__

__add__ 매직 메서드는 덧셈 연산이 어떻게 동작해야 하는지 정의합니다. 클래스 인스턴스에 + 연산자를 사용하면 이 메서드가 호출됩니다.

    ## ... (previous code in number.py)

    def __add__(self, other: 'MyNumber') -> 'MyNumber':
        """Returns the sum of the instance's value and the other instance's value."""
        return MyNumber(self.value + other.value)

__sub__

__sub__ 매직 메서드는 뺄셈 연산이 어떻게 동작해야 하는지 정의합니다. 클래스 인스턴스에 - 연산자를 사용하면 이 메서드가 호출됩니다.

    ## ... (previous code in number.py)

    def __sub__(self, other: 'MyNumber') -> 'MyNumber':
        """Returns the difference of the instance's value and the other instance's value."""
        return MyNumber(self.value - other.value)

__mul__

__mul__ 매직 메서드는 곱셈 연산이 어떻게 동작해야 하는지 정의합니다. 클래스 인스턴스에 * 연산자를 사용하면 이 메서드가 호출됩니다.

    ## ... (previous code in number.py)

    def __mul__(self, other: 'MyNumber') -> 'MyNumber':
        """Returns the product of the instance's value and the other instance's value."""
        return MyNumber(self.value * other.value)

__truediv__

__truediv__ 매직 메서드는 참 나눗셈 연산이 어떻게 동작해야 하는지 정의합니다. 클래스 인스턴스에 / 연산자를 사용하면 이 메서드가 호출됩니다.

    ## ... (previous code in number.py)

    def __truediv__(self, other: 'MyNumber') -> 'MyNumber':
        """Returns the result of dividing the instance's value by the other instance's value."""
        return MyNumber(self.value / other.value)

__floordiv__

__floordiv__ 매직 메서드는 정수 나눗셈 연산이 어떻게 동작해야 하는지 정의합니다. 클래스 인스턴스에 // 연산자를 사용하면 이 메서드가 호출됩니다.

    ## ... (previous code in number.py)

    def __floordiv__(self, other: 'MyNumber') -> 'MyNumber':
        """Returns the largest integer less than or equal to the result of dividing the instance's value by the other instance's value."""
        return MyNumber(self.value // other.value)

__mod__

__mod__ 매직 메서드는 모듈로 연산이 어떻게 동작해야 하는지 정의합니다. 클래스 인스턴스에 % 연산자를 사용하면 이 메서드가 호출됩니다.

    ## ... (previous code in number.py)

    def __mod__(self, other: 'MyNumber') -> 'MyNumber':
        """Returns the remainder of dividing the instance's value by the other instance's value."""
        return MyNumber(self.value % other.value)

__pow__

__pow__ 매직 메서드는 거듭제곱 연산이 어떻게 동작해야 하는지 정의합니다. 클래스 인스턴스에 ** 연산자 또는 pow() 함수를 사용하면 이 메서드가 호출됩니다.

    ## ... (previous code in number.py)

    def __pow__(self, other: 'MyNumber') -> 'MyNumber':
        """Returns the instance's value raised to the power of the other instance's value."""
        return MyNumber(self.value ** other.value)

예시: 이항 연산자 사용

MyNumber 클래스에 대한 이항 연산자를 정의했으므로, binary_example.py에서 어떻게 작동하는지 살펴보겠습니다.

from number import MyNumber

## Create two new MyNumber objects
a = MyNumber(5)
b = MyNumber(3)
print(f'{a.value=}, {b.value=}') ## Output: a.value=5, b.value=3

## Use the __add__ method with the print function
print(f'{(a+b).value=}')  ## Output: (a+b).value=8

## Use the __sub__ method with the print function
print(f'{(a-b).value=}')  ## Output: (a-b).value=2

## Use the __mul__ method with the print function
print(f'{(a*b).value=}')  ## Output: (a*b).value=15

## Use the __truediv__ method with the print function
print(f'{(a/b).value=}')  ## Output: (a/b).value=1.6666666666666667

## Use the __floordiv__ method with the print function
print(f'{(a//b).value=}')  ## Output: (a//b).value=1

## Use the __mod__ method with the print function
print(f'{(a%b).value=}')  ## Output: (a%b).value=2

## Use the __pow__ method with the print function
print(f'{(a**b).value=}')  ## Output: (a**b).value=125

그런 다음 터미널에서 다음 명령을 입력하여 스크립트를 실행합니다.

python binary_example.py

제자리 연산

제자리 연산은 새로운 객체를 생성하지 않고 객체의 값을 제자리에서 수정하는 연산입니다. +=, -=, *=, /= 등과 같은 확장 할당 연산자로 표시됩니다.

Python 클래스에 대해 제자리 연산자가 정의되지 않은 경우, 제자리 연산을 시도하면 대신 이항 연산자가 사용됩니다.

inplace_example1.py에 예시가 있습니다. 이항 연산자를 제자리 연산자로 변경합니다.

from number import MyNumber

## Create two new MyNumber objects
a = MyNumber(5)
b = MyNumber(3)
print(f'{a.value=}, {b.value=}') ## Output: a.value=5, b.value=3

a += b
## Use the __add__ method with the print function
print(f'after a+=b: {a.value=}')  ## Output:after a+=b: (a+b).value=8

예제를 실행하려면 터미널에 다음 명령을 입력합니다.

python inplace_example1.py

결과에서 += 연산을 시도했을 때 __add__가 사용되었음을 보여줍니다.

그런 다음 MyNumber에서 제자리 연산을 구현하고 해당 이항 연산자와의 동작이 약간 다릅니다.

__iadd__

__iadd__ 매직 메서드는 제자리 덧셈 연산이 어떻게 동작해야 하는지 정의합니다. 클래스 인스턴스에 += 연산자를 사용하면 이 메서드가 호출됩니다.

    ## ... (previous code in number.py)

    def __iadd__(self, other: 'MyNumber') -> 'MyNumber':
        """Adds the other instance's value to the instance's value in-place."""
        print(f'input: {self.value=}, {other.value=}')
        self.value += other.value
        print(f'after +=: {self.value=}')
        return self

__isub__

__isub__ 매직 메서드는 제자리 뺄셈 연산이 어떻게 동작해야 하는지 정의합니다. 클래스 인스턴스에 -= 연산자를 사용하면 이 메서드가 호출됩니다.

    ## ... (previous code in number.py)

    def __isub__(self, other: 'MyNumber') -> 'MyNumber':
        """Subtracts the other instance's value from the instance's value in-place."""
        print(f'input: {self.value=}, {other.value=}')
        self.value -= other.value
        print(f'after -=: {self.value=}')
        return self

__imul__

__imul__ 매직 메서드는 제자리 곱셈 연산이 어떻게 동작해야 하는지 정의합니다. 클래스 인스턴스에 *= 연산자를 사용하면 이 메서드가 호출됩니다.

    ## ... (previous code in number.py)

    def __imul__(self, other: 'MyNumber') -> 'MyNumber':
        """Multiplies the instance's value by the other instance's value in-place."""
        print(f'input: {self.value=}, {other.value=}')
        self.value *= other.value
        print(f'after *=: {self.value=}')
        return self

__itruediv__

__itruediv__ 매직 메서드는 제자리 참 나눗셈 연산이 어떻게 동작해야 하는지 정의합니다. 클래스 인스턴스에 /= 연산자를 사용하면 이 메서드가 호출됩니다.

    ## ... (previous code in number.py)

    def __itruediv__(self, other: 'MyNumber') -> 'MyNumber':
        """Divides the instance's value by the other instance's value in-place."""
        print(f'input: {self.value=}, {other.value=}')
        self.value /= other.value
        print(f'after /=: {self.value=}')
        return self

__ifloordiv__

__ifloordiv__ 매직 메서드는 제자리 정수 나눗셈 연산이 어떻게 동작해야 하는지 정의합니다. 클래스 인스턴스에 //= 연산자를 사용하면 이 메서드가 호출됩니다.

    ## ... (previous code in number.py)

    def __ifloordiv__(self, other: 'MyNumber') -> 'MyNumber':
        """Performs in-place floor division on the instance's value by the other instance's value."""
        print(f'input: {self.value=}, {other.value=}')
        self.value //= other.value
        print(f'after //=: {self.value=}')
        return self

__imod__

__imod__ 매직 메서드는 제자리 모듈로 연산이 어떻게 동작해야 하는지 정의합니다. 클래스 인스턴스에 %= 연산자를 사용하면 이 메서드가 호출됩니다.

    ## ... (previous code in number.py)

    def __imod__(self, other: 'MyNumber') -> 'MyNumber':
        """Performs in-place modulo operation on the instance's value by the other instance's value."""
        print(f'input: {self.value=}, {other.value=}')
        self.value %= other.value
        print(f'after %=: {self.value=}')
        return self

__ipow__

__ipow__ 매직 메서드는 제자리 거듭제곱 연산이 어떻게 동작해야 하는지 정의합니다. 클래스 인스턴스에 **= 연산자를 사용하면 이 메서드가 호출됩니다.

    ## ... (previous code in number.py)

    def __ipow__(self, other: 'MyNumber') -> 'MyNumber':
        """Raises the instance's value to the power of the other instance's value in-place."""
        print(f'input: {self.value=}, {other.value=}')
        self.value **= other.value
        print(f'after **=: {self.value=}')
        return self

예시: 제자리 연산 사용

MyNumber 클래스에 대한 제자리 연산자를 정의했으므로, inplace_example2.py에서 어떻게 작동하는지 살펴보겠습니다.

from number import MyNumber

## Create a new MyNumber objects
a = MyNumber(13)

## Use the __iadd__ method
a += MyNumber(5)
## Output:
## input: self.value=13, other.value=5
## after +=: self.value=18

## Use the __isub__ method
a -= MyNumber(5)
## Output:
## input: self.value=18, other.value=5
## after -=: self.value=13

## Use the __imul__ method
a *= MyNumber(5)
## Output:
## input: self.value=13, other.value=5
## after *=: self.value=65

## Use the __itruediv__ method
a /= MyNumber(5)
## Output:
## input: self.value=65, other.value=5
## after /=: self.value=13.0

## Use the __ifloordiv__ method
a //= MyNumber(2)
## Output:
## input: self.value=13.0, other.value=2
## after //=: self.value=6.0

## Use the __imod__ method
a %= MyNumber(4)
## Output:
## input: self.value=6.0, other.value=4
## after %=: self.value=2.0

## Use the __ipow__ method
a **= MyNumber(3)
## Output:
## input: self.value=2.0, other.value=3
## after **=: self.value=8.0

그런 다음 터미널에서 다음 명령을 입력하여 스크립트를 실행합니다.

python inplace_example2.py

요약

이 튜토리얼에서는 다양한 유형의 숫자 연산과 상호 작용할 때 클래스에 대한 사용자 정의 동작을 정의할 수 있는 숫자 연산과 관련된 Python 매직 메서드를 살펴보았습니다. 단항 연산자, 이항 연산자 및 제자리 연산을 다루었으며, 각 매직 메서드를 구현하는 방법을 배웠습니다.

사용자 정의 클래스에서 이러한 매직 메서드를 구현하면 표준 Python 연산과 원활하게 작동하는 직관적이고 사용하기 쉬운 객체를 만들 수 있습니다. 이는 코드의 가독성을 향상시킬 뿐만 아니라 유지 관리 및 사용자 친화성을 높입니다.

Python 기술을 계속 개발하면서 클래스의 동작을 더욱 사용자 정의하고 더욱 강력한 추상화를 만들기 위해 다른 매직 메서드를 실험해 보십시오.