상위 목록: 하위 목록: 작성 날짜: 읽는 데 13 분 소요

매직 메서드(Magic Method)

매직 메서드(Magic Method)는 미리 정의되어 있는 메서드들을 재정의하여 클래스를 활용할 수 있도록 변경합니다.

내장 함수들이 처리하는 연산을 변경해 사용자 정의 클래스나 함수 등을 효율적으로 사용할 수 있습니다.

메서드 이름의 앞뒤에 밑줄(Underscore, _)을 두 개씩 붙여(__이름__) 매직 메서드를 정의할 수 있습니다.

제 35강에서는 인스턴스의 생성과 문자열 표현을 담당하는 매직 메서드를 다뤘습니다. 이번 강좌에서는 +, -, <, & 같은 연산자와 int(), bool() 같은 형식 변환을 담당하는 매직 메서드를 다룹니다.

3 + 5나 "a" + "b"처럼 같은 + 연산자라도 형식에 따라 덧셈이 되기도 하고 연결이 되기도 합니다. 이는 int와 str이 각자 다른 __add__를 정의하고 있기 때문입니다.

마찬가지로 직접 만든 클래스에 연산자 매직 메서드를 정의하면, 벡터의 덧셈이나 버전 비교처럼 그 클래스에 맞는 의미의 연산을 연산자로 표현할 수 있습니다. 이를 연산자 오버로딩(Operator Overloading)이라 부릅니다.

  • Tip : 매직 메서드는 이중 밑줄(Double Underscore)을 사용해 정의하므로 던더 메서드(Dunder Method)라고도 부릅니다.



산술 연산 정의

산술 연산자는 하나의 연산마다 세 가지 매직 메서드를 가집니다.

x + y를 위한 정방향 __add__, y + x처럼 인스턴스가 오른쪽에 있을 때를 위한 역방향 __radd__, x += y를 위한 복합 대입 __iadd__입니다.

다음 예제는 세 메서드가 각각 어떤 문법에서 호출되는지 확인하기 위해, 일부러 서로 다른 방식으로 계산하도록 정의했습니다.


class Daeheeyun:
    def __init__(self, value):
        self.value = value

    def __add__(self, other):
        return self.value + int(other)

    def __radd__(self, other):
        return self.value + other

    def __iadd__(self, other):
        return self.value + abs(other)


instance = Daeheeyun(5)
add = instance + 3.076923
radd = 3.076923 + instance
instance += -3.076923

print(add)
print(radd)
print(instance)
결과
8
8.076923
8.076923


정방향 연산자

정방향 연산자는 x + y에 대한 연산을 정의합니다.

인스턴스의 값이 x가 되며, 외부 값이 y가 됩니다.

예제는 y를 int로 변환 후, 연산합니다.

instance + 3.076923은 instance.__add__(3.076923)으로 호출되어 5 + 3의 결과인 8이 됩니다.

정방향 연산자의 self는 연산자의 왼쪽 피연산자이고, other는 오른쪽 피연산자입니다. Python은 항상 왼쪽 피연산자의 정방향 연산자를 먼저 시도합니다.


역방향 연산자

역방향 연산자는 y + x에 대한 연산을 정의합니다.

인스턴스의 값이 x가 되며, 외부 값이 y가 됩니다.

3.076923 + instance는 먼저 float의 __add__를 호출하지만, float은 Daeheeyun과의 연산 방법을 모르므로 NotImplemented를 반환합니다.

이 경우 Python은 오른쪽 피연산자의 역방향 연산자인 instance.__radd__(3.076923)를 호출합니다.

예제는 y를 변환 없이 연산합니다. 그러므로 5 + 3.076923의 결과인 8.076923이 됩니다.

역방향 연산자의 self는 연산자의 오른쪽 피연산자이고, other는 왼쪽 피연산자입니다. 덧셈이나 곱셈처럼 순서가 상관없는 연산이 아니라면, __rsub__나 __rtruediv__에서는 other - self처럼 순서를 뒤집어 계산해야 합니다.

  • Tip : 직접 정의한 정방향 연산자에서 처리할 수 없는 형식이 전달되면, 오류를 발생시키는 대신 return NotImplemented를 반환해 상대 객체의 역방향 연산자가 시도되도록 할 수 있습니다.


복합 대입 연산자

복합 대입 연산자는 x += y에 대한 연산을 정의합니다.

인스턴스의 값이 x가 되며, 외부 값이 y가 됩니다.

예제는 y를 절댓값으로 변환하고 연산합니다.

복합 대입 연산자는 반환값이 x에 다시 할당됩니다. 예제는 숫자를 반환하므로 instance는 더 이상 Daeheeyun 인스턴스가 아닌 float 값 8.076923이 됩니다.

일반적으로 복합 대입 연산자는 자기 자신을 수정한 뒤 self를 반환하도록 정의합니다.

__iadd__가 정의되지 않았다면, x += y는 x = x + y처럼 __add__를 사용해 처리됩니다.


class Daeheeyun:
    def __init__(self, value):
        self.value = value

    def __iadd__(self, other):
        self.value += other


instance = Daeheeyun(5)
instance += 3
print(instance)
결과
None

복합 대입 연산자에서 가장 흔한 실수는 return self를 빠뜨리는 것입니다.

instance += 3은 instance = instance.__iadd__(3)처럼 동작하므로, return 문이 없으면 None이 instance에 다시 할당됩니다. 값은 정상적으로 더해졌지만, 인스턴스 자체를 잃어버리게 됩니다.


NotImplemented 활용

class Vector:
    def __init__(self, x, y):
        self.x = x
        self.y = y

    def __repr__(self):
        return f"Vector({self.x}, {self.y})"

    def __add__(self, other):
        if isinstance(other, Vector):
            return Vector(self.x + other.x, self.y + other.y)
        return NotImplemented

    def __mul__(self, other):
        if isinstance(other, (int, float)):
            return Vector(self.x * other, self.y * other)
        return NotImplemented

    def __rmul__(self, other):
        return self.__mul__(other)


a = Vector(1, 2)
b = Vector(3, 4)
print(a + b)
print(a * 3)
print(3 * a)
try:
    a + 1
except TypeError as e:
    print("TypeError:", e)
결과
Vector(4, 6)
Vector(3, 6)
Vector(3, 6)
TypeError: unsupported operand type(s) for +: ‘Vector’ and ‘int’

Vector는 실제로 사용할 수 있는 형태로 연산자를 정의한 예제입니다. 연산 결과로 새로운 Vector 인스턴스를 반환하므로, 원본인 a와 b는 변경되지 않습니다.

a + b는 a.__add__(b)가 호출되어 각 성분을 더한 Vector(4, 6)이 반환됩니다.

a * 3은 a.__mul__(3)이, 3 * a는 int의 __mul__이 NotImplemented를 반환한 뒤 a.__rmul__(3)이 호출됩니다. 곱셈은 순서가 상관없으므로 __rmul__은 __mul__을 그대로 호출합니다.

a + 1은 __add__가 NotImplemented를 반환하고, int에도 Vector를 처리하는 __radd__가 없으므로 Python이 TypeError를 발생시킵니다.

NotImplemented는 오류가 아닌 “이 형식은 처리할 수 없다”는 신호 값입니다. 직접 raise TypeError를 발생시키면 상대 객체의 역방향 연산자가 시도될 기회가 사라지므로, 처리할 수 없는 형식은 NotImplemented를 반환하는 것이 좋습니다.

  • Tip : NotImplemented는 값이므로 return으로 반환합니다. 이름이 비슷한 NotImplementedError는 아직 구현되지 않은 메서드에서 raise로 발생시키는 예외이므로 혼동하지 않아야 합니다.


가변 객체와 불변 객체의 복합 대입

a = [1, 2]
alias = a
a += [3]
print(a, alias)

t = (1, 2)
alias = t
t += (3,)
print(t, alias)
결과
[1, 2, 3] [1, 2, 3]
(1, 2, 3) (1, 2)

같은 +=라도 객체가 __iadd__를 정의했는지에 따라 결과가 달라집니다.

list는 __iadd__를 정의하고 있어 원본 리스트를 직접 수정한 뒤 자기 자신을 반환합니다. 그러므로 같은 리스트를 가리키는 alias에도 3이 추가됩니다.

tuple은 불변 형식이므로 __iadd__가 없습니다. 그러므로 t += (3,)은 t = t + (3,)으로 처리되어 새로운 튜플이 만들어지고, alias는 원래 튜플을 그대로 가리킵니다.

직접 만든 클래스도 같은 규칙을 따릅니다. 원본을 수정하는 동작이 필요하면 __iadd__를 정의하고, 항상 새 객체를 만들어야 하는 불변 객체라면 __add__만 정의합니다.


산술 연산자 의미 역방향 복합 대입
object.__add__(self, other) object + other 연산 정의 __radd__ __iadd__
object.__sub__(self, other) object - other 연산 정의 __rsub__ __isub__
object.__mul__(self, other) object * other 연산 정의 __rmul__ __imul__
object.__matmul__(self, other) object @ other 연산 정의 __rmatmul__ __imatmul__
object.__truediv__(self, other) object / other 연산 정의 __rtruediv__ __itruediv__
object.__floordiv__(self, other) object // other 연산 정의 __rfloordiv__ __ifloordiv__
object.__mod__(self, other) object % other 연산 정의 __rmod__ __imod__
object.__pow__(self, other[, modulo]) pow() 연산 정의 __rpow__ __ipow__
object.__divmod__(self, other) divmod() 연산 정의 __rdivmod__ 없음
object.__round__(self[, ndigits]) round() 연산 정의 없음 없음
object.__trunc__(self) math.trunc() 연산 정의 없음 없음
object.__floor__(self) math.floor() 연산 정의 없음 없음
object.__ceil__(self) math.ceil() 연산 정의 없음 없음
  • Tip : divmod()는 복합 대입 연산자가 없으므로 __idivmod__는 존재하지 않습니다.

  • Tip : Python 3.14 이상부터 pow(x, y, mod)처럼 인자가 세 개인 pow()도 y의 __rpow__를 호출할 수 있습니다.

  • Tip : @ 연산자는 Python 3.5에서 행렬 곱셈을 위해 추가되었으며, 내장 형식 중에는 이 연산자를 지원하는 형식이 없습니다. 주로 numpy 배열에서 사용합니다.



단항 연산 정의

단항 연산자는 피연산자가 하나인 연산자입니다. -x, +x, ~x, abs(x)가 해당하며, 상대 피연산자가 없으므로 self 외의 매개변수를 받지 않습니다.

다음 예제는 각 메서드가 호출되는 것을 확인하기 위해, 일부러 원래의 의미와 다르게 계산하도록 정의했습니다.


class Daeheeyun:
    def __init__(self, value):
        self.value = value

    def __pos__(self):
        return self.value + 2

    def __neg__(self):
        return self.value - 2

    def __abs__(self):
        return -abs(self.value)

    def __invert__(self):
        return ~self.value


instance = Daeheeyun(5)
print(+instance)
print(-instance)
print(abs(instance))
print(~instance)
결과
7
3
-5
-6


__pos__ : Positive

+object 연산자는 +x에 대한 연산을 정의합니다.

예제는 x를 양수를 곱하지 않고, +2 연산으로 변경합니다. 그러므로 +instance는 5 + 2인 7이 됩니다.


__neg__ : Negative

-object 연산자는 -x에 대한 연산을 정의합니다.

예제는 x를 음수를 곱하지 않고, -2 연산으로 변경합니다. 그러므로 -instance는 5 - 2인 3이 됩니다.


__abs__ : Absolute

abs() 연산자는 abs(x)에 대한 연산을 정의합니다.

예제는 x를 절대값으로 취하지 않고, 음수로 변경합니다. 그러므로 abs(instance)는 -5가 됩니다.


__invert__ : Invert

~object 연산자는 ~x에 대한 연산을 정의합니다.

예제는 ~x를 변환 없이 연산합니다. 정수의 ~x는 모든 비트를 반전하며, 결과는 -(x + 1)과 같으므로 ~5는 -6이 됩니다.


class Money:
    def __init__(self, amount):
        self.amount = amount

    def __neg__(self):
        return Money(-self.amount)

    def __abs__(self):
        return Money(abs(self.amount))

    def __repr__(self):
        return f"Money({self.amount})"


debt = Money(-500)
print(-debt)
print(abs(debt))
print(debt)
결과
Money(500)
Money(500)
Money(-500)

실제로 단항 연산자를 정의할 때는 원래의 의미를 지키고, 예제처럼 같은 클래스의 새 인스턴스를 반환하는 것이 일반적입니다.

-debt와 abs(debt)는 각각 새로운 Money(500)을 반환하며, 마지막 줄처럼 원본인 debt는 -500으로 그대로 유지됩니다.

단항 연산의 결과로 정수를 반환하면 -(-debt)처럼 연산을 이어서 적용할 때 Money의 다른 기능을 사용할 수 없으므로, 같은 형식을 반환해야 연산을 자연스럽게 연결할 수 있습니다.


단항 연산자 의미
object.__pos__(self) +object 연산 정의
object.__neg__(self) -object 연산 정의
object.__abs__(self) abs() 연산 정의
object.__invert__(self) ~object 연산 정의



비트 연산 정의

비트 연산자는 정수의 비트 단위 연산인 <<, >>, &, |, ^를 정의합니다.

and, or, not은 비트 연산자가 아닌 논리 연산자이며, 매직 메서드로 직접 재정의할 수 없습니다.

not x의 결과는 __bool__(정의되지 않았다면 __len__)의 결과에 따라 결정되므로, __bool__을 재정의해 간접적으로 변경할 수 있습니다.

비트 반전 연산자인 ~object는 단항 연산자이므로 __invert__로 정의합니다.

비트 연산자도 산술 연산자와 마찬가지로 정방향, 역방향, 복합 대입 메서드를 가집니다. 정수가 아닌 클래스에서도 집합의 |(합집합), &(교집합)처럼 “합치기”와 “공통 부분”의 의미로 자주 재정의됩니다.


class Permission:
    def __init__(self, bits):
        self.bits = bits

    def __or__(self, other):
        return Permission(self.bits | other.bits)

    def __and__(self, other):
        return Permission(self.bits & other.bits)

    def __repr__(self):
        return f"Permission({self.bits:03b})"


READ = Permission(0b100)
WRITE = Permission(0b010)
user = READ | WRITE
print(user)
print(user & WRITE)
print(user & Permission(0b001))
결과
Permission(110)
Permission(010)
Permission(000)

Permission은 읽기, 쓰기, 실행 권한을 각각 하나의 비트로 표현하는 클래스입니다.

READ | WRITE는 READ.__or__(WRITE)가 호출되어 두 비트를 합친 110이 됩니다.

user & WRITE는 __and__가 호출되어 공통으로 켜진 비트인 010만 남으므로, 사용자에게 쓰기 권한이 있음을 확인할 수 있습니다. 실행 권한 001과의 & 결과는 000이므로 실행 권한은 없습니다.

__repr__의 {self.bits:03b}는 정수를 세 자리 2진수로 표현하는 형식입니다.

  • Tip : 실제 프로그램에서 이러한 권한 조합은 enum.Flag로 간단하게 정의할 수 있습니다. 열거형은 제 29강에서 확인할 수 있습니다.


비트 연산자 의미 역방향 복합 대입
object.__lshift__(self, other) object « other 연산 정의 __rlshift__ __ilshift__
object.__rshift__(self, other) object » other 연산 정의 __rrshift__ __irshift__
object.__and__(self, other) object & other 연산 정의 __rand__ __iand__
object.__or__(self, other) object | other 연산 정의 __ror__ __ior__
object.__xor__(self, other) object ^ other 연산 정의 __rxor__ __ixor__



비교 연산 정의

비교 연산자는 __radd__ 같은 별도의 역방향 연산자와 복합 대입 연산자가 존재하지 않습니다.

대신, 서로 반대가 되는 비교 연산자가 역방향 연산자의 역할을 합니다.

비교 연산자를 정의하면 ==, < 같은 연산자뿐만 아니라, 비교를 내부적으로 사용하는 sorted(), max(), min(), in 연산 등에서도 직접 만든 클래스를 사용할 수 있습니다.


class Daeheeyun:
    def __init__(self, value):
        self.value = value

    def __lt__(self, other):
        print("__lt__ 호출")
        return self.value < other

    def __gt__(self, other):
        print("__gt__ 호출")
        return self.value > other


instance = Daeheeyun(5)
print(instance < 10)
print(10 < instance)
결과
__lt__ 호출
True
__gt__ 호출
False

instance < 10은 instance.__lt__(10)으로 호출됩니다.

10 < instance는 int가 Daeheeyun과의 비교 방법을 모르므로, 반대 연산자인 instance.__gt__(10)으로 호출됩니다.

10 < instance와 instance > 10은 같은 의미이므로, __gt__가 5 > 10을 계산해 False를 반환합니다.

이처럼 __lt__와 __gt__, __le__와 __ge__가 서로의 역방향 역할을 하며, __eq__와 __ne__는 자기 자신이 역방향 역할을 합니다.

  • Tip : __eq__만 정의하면 __ne__는 __eq__의 결과를 반전해 자동으로 처리됩니다. 나머지 비교 연산자를 한 번에 채우려면 functools.total_ordering 데코레이터를 사용할 수 있습니다.


__eq__ 와 __hash__

class Point:
    def __init__(self, x, y):
        self.x = x
        self.y = y


print(Point(1, 2) == Point(1, 2))


class Point:
    def __init__(self, x, y):
        self.x = x
        self.y = y

    def __eq__(self, other):
        if not isinstance(other, Point):
            return NotImplemented
        return (self.x, self.y) == (other.x, other.y)


print(Point(1, 2) == Point(1, 2))
print(Point(1, 2) != Point(3, 4))
print(Point.__hash__)
try:
    {Point(1, 2)}
except TypeError as e:
    print("TypeError:", e)
결과
False
True
True
None
TypeError: cannot use ‘Point’ as a set element (unhashable type: ‘Point’)

__eq__를 정의하지 않으면, ==는 두 객체가 같은 객체인지(is)를 비교합니다. 그러므로 값이 같아도 서로 다른 인스턴스인 첫 번째 비교는 False입니다.

__eq__를 정의하면 x, y 값을 비교하므로 True가 되며, __ne__를 정의하지 않았지만 !=도 __eq__를 반전해 True를 반환합니다.

주의할 점은 __eq__만 정의하면 __hash__가 자동으로 None이 된다는 것입니다. 해시값이 없는 객체는 집합의 원소나 사전의 키로 사용할 수 없으므로 TypeError가 발생합니다.

같다고 판단되는 두 객체는 반드시 같은 해시값을 가져야 집합과 사전이 올바르게 동작합니다. 기존 __hash__는 객체마다 다른 값을 반환하므로, __eq__를 바꾸면 Python이 일관성을 위해 __hash__를 제거합니다.


class Point:
    def __init__(self, x, y):
        self.x = x
        self.y = y

    def __eq__(self, other):
        if not isinstance(other, Point):
            return NotImplemented
        return (self.x, self.y) == (other.x, other.y)

    def __hash__(self):
        return hash((self.x, self.y))


points = {Point(1, 2), Point(1, 2), Point(3, 4)}
print(len(points))
print(Point(1, 2) in points)
결과
2
True

__hash__에서 __eq__가 비교하는 값과 같은 값들로 해시값을 계산하면, 집합과 사전에서도 사용할 수 있습니다.

Point(1, 2) 두 개는 해시값과 __eq__ 결과가 모두 같으므로 하나로 취급되어, 집합의 원소 개수는 2가 됩니다.

단, 해시값을 계산하는 데 사용한 속성을 나중에 변경하면 집합에서 찾을 수 없게 되므로, __hash__를 정의한 객체는 해당 속성을 변경하지 않는 것이 좋습니다.


total_ordering

from functools import total_ordering


@total_ordering
class Version:
    def __init__(self, major, minor):
        self.major = major
        self.minor = minor

    def __eq__(self, other):
        return (self.major, self.minor) == (other.major, other.minor)

    def __lt__(self, other):
        return (self.major, self.minor) < (other.major, other.minor)

    def __repr__(self):
        return f"{self.major}.{self.minor}"


versions = [Version(3, 14), Version(3, 7), Version(3, 12)]
print(sorted(versions))
print(max(versions))
print(Version(3, 14) >= Version(3, 12))
결과
[3.7, 3.12, 3.14]
3.14
True

@total_ordering은 __eq__와 __lt__(또는 __le__, __gt__, __ge__ 중 하나)만 정의하면 나머지 비교 연산자를 자동으로 만들어 줍니다.

sorted()는 내부적으로 < 비교를 사용하므로 직접 정의한 __lt__로 정렬합니다. 반면 max()는 > 비교를 사용하므로 total_ordering이 만들어 준 __gt__로, >=는 __ge__로 처리됩니다.

total_ordering이 없다면 __gt__와 __ge__가 없으므로, max()는 역방향 연산자인 __lt__로 대신 처리되지만 >= 비교는 __ge__와 그 역방향인 __le__가 모두 없어 TypeError가 발생합니다.

예제는 버전을 (major, minor) 튜플로 비교합니다. 튜플은 앞 요소부터 차례로 비교하므로, 문자열 "3.7"과 "3.12"를 비교할 때와 달리 3.7이 3.12보다 작다고 올바르게 판단합니다.


비교 연산자 의미
object.__lt__(self, other) object < other 연산 정의
object.__le__(self, other) object <= other 연산 정의
object.__eq__(self, other) object == other 연산 정의
object.__ne__(self, other) object != other 연산 정의
object.__gt__(self, other) object > other 연산 정의
object.__ge__(self, other) object >= other 연산 정의



형식 변환 정의

class Daeheeyun:
    def __init__(self, value):
        self.value = value

    def __index__(self):
        return 2


L = ["A", "B", "C", "D", "E"]
instance = Daeheeyun(100)
print(L[instance])
결과
C

형식 변환은 인스턴스의 형식을 변환할 때 사용됩니다.

그 중, __index__는 객체를 정수로 손실 없이 변환하는 방법을 정의하며, 리스트의 인덱싱이나 슬라이싱에서 정수 대신 사용될 때 호출됩니다.

예제의 index의 값을 2로 정의하여, 리스트에서 C가 출력됩니다.


__int__, __float__, __bool__

class Score:
    def __init__(self, value):
        self.value = value

    def __int__(self):
        return int(self.value)

    def __float__(self):
        return float(self.value)

    def __bool__(self):
        return self.value > 0


score = Score(87.5)
print(int(score), float(score))
print(bool(score), bool(Score(0)))
if not Score(-1):
    print("점수가 없습니다.")
try:
    bin(score)
except TypeError as e:
    print("TypeError:", e)
결과
87 87.5
True False
점수가 없습니다.
TypeError: ‘Score’ object cannot be interpreted as an integer

int(score)는 __int__를, float(score)는 __float__를 호출합니다. __int__는 87.5를 정수로 변환하므로 소수점 아래가 버려진 87을 반환합니다.

bool(score)는 __bool__을 호출하며, 예제는 값이 0보다 클 때만 True를 반환합니다.

__bool__은 bool()뿐만 아니라 if, while, not처럼 참·거짓을 판단하는 모든 곳에서 호출됩니다. 그러므로 if not Score(-1): 조건이 참이 되어 메시지가 출력됩니다.

bin(), hex(), 인덱싱은 __int__가 아닌 __index__를 요구하므로, __int__만 정의한 Score는 TypeError가 발생합니다.


__int__ 와 __index__의 차이

class Daeheeyun:
    def __index__(self):
        return 10


instance = Daeheeyun()
print(bin(instance), hex(instance))
print(list(range(instance)))
print(int(instance))
print("ABCDEFGHIJKL"[instance:])
결과
0b1010 0xa
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
10
KL

__index__를 정의하면 bin(), hex(), range(), 슬라이싱처럼 정수만 허용하는 곳에서 인스턴스를 사용할 수 있습니다.

__int__가 없어도 int(instance)가 10을 반환하는 이유는, int()가 __int__가 없을 때 __index__를 대신 사용하기 때문입니다.

__int__는 87.5 → 87처럼 값을 잃어도 되는 변환에, __index__는 객체가 원래 정수와 같은 의미를 가질 때 사용합니다. 예를 들어 float은 __int__는 있지만 __index__가 없으므로 [1, 2][1.0]처럼 인덱스로 사용할 수 없습니다.


class Bag:
    def __init__(self, items):
        self.items = items

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


print(bool(Bag([])), bool(Bag([1, 2])))


class Empty:
    pass


print(bool(Empty()))
결과
False True
True

__bool__이 정의되지 않았다면, bool()은 __len__의 결과가 0인지 확인합니다. 그러므로 빈 Bag은 False, 요소가 있는 Bag은 True입니다.

__bool__과 __len__이 모두 없다면 객체는 항상 True로 판단됩니다.


형식 변환 의미
object.__int__(self) int() 연산 정의
object.__float__(self) float() 연산 정의
object.__complex__(self) complex() 연산 정의
object.__bool__(self) bool() 연산 정의
object.__hash__(self) hash() 연산 정의
object.__index__(self) 인덱싱, 슬라이싱, bin(), hex() 등의 정수 변환 정의
  • Tip : Python 3.14 이상부터 int()는 더 이상 __trunc__를 대신 호출하지 않습니다. int()로 변환할 수 있게 하려면 __int__나 __index__를 정의해야 합니다.



정리

분류 매직 메서드 호출하는 문법/내장 함수 특징
산술 __add__, __sub__, __mul__ 등 x + y, x - y, x * y 처리 못 하면 NotImplemented 반환
역방향 __radd__, __rsub__ 등 y + x (왼쪽이 처리 못 할 때) self가 오른쪽 피연산자
복합 대입 __iadd__, __isub__ 등 x += y 반환값이 x에 할당, 보통 self 반환
단항 __neg__, __pos__, __abs__, __invert__ -x, +x, abs(x), ~x 매개변수는 self만
비트 __and__, __or__, __xor__ 등 x & y, x | y, x ^ y 역방향·복합 대입 존재
비교 __eq__, __lt__ 등 ==, <, sorted(), max() __eq__ 정의 시 __hash__는 None
형식 변환 __int__, __float__, __bool__ int(), float(), bool(), if 반환 형식이 정해져 있음
정수 변환 __index__ 인덱싱, 슬라이싱, bin(), range() 손실 없는 정수 변환

연산자 매직 메서드는 원본을 바꾸지 않고 새 객체를 반환하는 것이 기본이며, 원본을 수정하는 동작은 복합 대입 메서드에서만 처리합니다.

처리할 수 없는 형식은 오류 대신 NotImplemented를 반환해, Python이 상대 객체의 역방향 메서드를 시도하도록 합니다.

댓글 남기기