본문 바로가기

Python study

연산자 오버로딩

단항 연산자 오버로딩

벡터(Vector) 클래스를 만들고, 몇 가지 단항 연산자를 오버로딩해보겠다. 이를 통해 부호 변경(-), 절대값(abs), 그리고 반전(~) 연산자를 벡터에 대해 사용할 수 있도록 하겠다.

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

    def __neg__(self):
        """부호 변경: -벡터"""
        return Vector(-self.x, -self.y)

    def __abs__(self):
        """벡터의 절대값 (유클리드 거리)"""
        return (self.x ** 2 + self.y ** 2) ** 0.5

    def __invert__(self):
        """비트 반전: ~벡터 (정수로 가정)"""
        return Vector(~self.x, ~self.y)

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

# 사용 예시
v = Vector(3, -4)
print(-v)     # Vector(-3, 4)
print(abs(v)) # 5.0 (3-4-5 삼각형의 피타고라스 거리)
print(~v)     # Vector(-4, 3)

이 벡터 클래스에는 세 가지 단항 연산자가 오버로딩되어 있다:

  1. neg: 벡터의 부호를 반전한다.
  2. abs: 벡터의 절대값(유클리드 거리)을 계산한다.
  3. invert: 벡터의 각 성분의 비트를 반전한다. (여기서는 성분이 정수라고 가정한다.)

이제 벡터 클래스의 인스턴스를 만들고 단항 연산자를 사용할 수 있다. 예를 들어, 벡터 v에 대해 -v는 벡터의 각 성분의 부호를 반전시키고, abs(v)는 벡터의 크기를 계산하며, ~v는 각 성분의 비트를 반전시킨다.

 

+ 연산자 오버로딩

벡터 클래스에 덧셈 연산자(+)를 오버로딩하여, 길이가 다른 벡터도 더할 수 있도록 하겠다. 이를 위해 짧은 벡터의 남는 부분은 0으로 채우는 방식으로 구현하겠다.

class Vector:
    def __init__(self, *components):
        self.components = list(components)

    def __neg__(self):
        """부호 변경: -벡터"""
        return Vector(*[-c for c in self.components])

    def __abs__(self):
        """벡터의 절대값 (유클리드 거리)"""
        return sum(c ** 2 for c in self.components) ** 0.5

    def __invert__(self):
        """비트 반전: ~벡터 (정수로 가정)"""
        return Vector(*[~c for c in self.components])

    def __add__(self, other):
        """벡터 덧셈: 길이가 다른 벡터도 더할 수 있도록 구현"""
        max_len = max(len(self.components), len(other.components))
        new_components = [
            (self.components[i] if i < len(self.components) else 0) +
            (other.components[i] if i < len(other.components) else 0)
            for i in range(max_len)
        ]
        return Vector(*new_components)

    def __repr__(self):
        return f"Vector({', '.join(map(str, self.components))})"

# 사용 예시
v1 = Vector(1, 2, 3)
v2 = Vector(4, 5)
v3 = v1 + v2
print(v3)  # Vector(5, 7, 3)

v4 = Vector(1, 2)
v5 = Vector(3, 4, 5, 6)
v6 = v4 + v5
print(v6)  # Vector(4, 6, 5, 6)

print(-v1)      # Vector(-1, -2, -3)
print(abs(v1))  # 3.7416573867739413
print(~Vector(3, -4)) # Vector(-4, 3)

 

순서 바뀌어도 + 연산 가능

연산자 순서가 바뀌어도 벡터가 더해질 수 있도록 radd 메서드도 오버로딩해보겠다. 이를 통해, 벡터 객체가 오른쪽 피연산자로 있을 때도 덧셈이 가능하도록 한다.

class Vector:
    def __init__(self, *components):
        self.components = list(components)

    def __neg__(self):
        """부호 변경: -벡터"""
        return Vector(*[-c for c in self.components])

    def __abs__(self):
        """벡터의 절대값 (유클리드 거리)"""
        return sum(c ** 2 for c in self.components) ** 0.5

    def __invert__(self):
        """비트 반전: ~벡터 (정수로 가정)"""
        return Vector(*[~c for c in self.components])

    def __add__(self, other):
        """벡터 덧셈: 길이가 다른 벡터도 더할 수 있도록 구현"""
        if isinstance(other, Vector):
            max_len = max(len(self.components), len(other.components))
            new_components = [
                (self.components[i] if i < len(self.components) else 0) +
                (other.components[i] if i < len(other.components) else 0)
                for i in range(max_len)
            ]
            return Vector(*new_components)
        else:
            # 다른 타입의 객체와 덧셈
            return NotImplemented

    def __radd__(self, other):
        """벡터 덧셈: other + 벡터"""
        return self.__add__(other)

    def __repr__(self):
        return f"Vector({', '.join(map(str, self.components))})"

# 사용 예시
v1 = Vector(1, 2, 3)
v2 = Vector(4, 5)
v3 = v1 + v2
print(v3)  # Vector(5, 7, 3)

v4 = Vector(1, 2)
v5 = Vector(3, 4, 5, 6)
v6 = v4 + v5
print(v6)  # Vector(4, 6, 5, 6)

print(-v1)      # Vector(-1, -2, -3)
print(abs(v1))  # 3.7416573867739413
print(~Vector(3, -4)) # Vector(-4, 3)

# __radd__ 예시
v7 = Vector(1, 2, 3)
v8 = Vector(4, 5, 6)
print(v7 + v8)  # Vector(5, 7, 9)
print(v8 + v7)  # Vector(5, 7, 9)

이제 벡터 클래스는 radd 메서드를 포함하고 있어서, 벡터가 오른쪽 피연산자로 있을 때도 덧셈이 가능하다. add 메서드와 동일한 로직을 사용하여 벡터의 덧셈을 처리한다.

예를 들어, v1 + v2와 v2 + v1 모두 정상적으로 작동한다.

 

* 연산자 오버로딩

벡터 클래스에 스칼라와의 곱셈을 위한 * 연산자도 오버로딩해보겠다. 이를 위해 mulrmul 메서드를 추가하여 스칼라와 벡터의 곱셈을 처리할 수 있도록 한다. 또한 스칼라가 int 또는 float 타입인지 확인하기 위해 numbers.Real을 사용하여 타입을 검사하는 코드를 작성하겠다.

import numbers

class Vector:
    def __init__(self, *components):
        self.components = list(components)
    
    def __neg__(self):
        """부호 변경: -벡터"""
        return Vector(*[-c for c in self.components])
    
    def __abs__(self):
        """벡터의 절대값 (유클리드 거리)"""
        return sum(c ** 2 for c in self.components) ** 0.5
    
    def __invert__(self):
        """비트 반전: ~벡터 (정수로 가정)"""
        return Vector(*[~c for c in self.components])
    
    def __add__(self, other):
        """벡터 덧셈: 길이가 다른 벡터도 더할 수 있도록 구현"""
        if isinstance(other, Vector):
            max_len = max(len(self.components), len(other.components))
            new_components = [
                (self.components[i] if i < len(self.components) else 0) +
                (other.components[i] if i < len(other.components) else 0)
                for i in range(max_len)
            ]
            return Vector(*new_components)
        else:
            # 다른 타입의 객체와 덧셈
            return NotImplemented
    
    def __radd__(self, other):
        """벡터 덧셈: other + 벡터"""
        return self.__add__(other)
    
    def __mul__(self, other):
        """스칼라 곱: 벡터 * 스칼라"""
        if isinstance(other, numbers.Real):
            new_components = [c * other for c in self.components]
            return Vector(*new_components)
        else:
            return NotImplemented
    
    def __rmul__(self, other):
        """스칼라 곱: 스칼라 * 벡터"""
        return self.__mul__(other)
    
    def __repr__(self):
        return f"Vector({', '.join(map(str, self.components))})"

# 사용 예시
v1 = Vector(1, 2, 3)
v2 = Vector(4, 5)
v3 = v1 + v2
print(v3)  # Vector(5, 7, 3)

v4 = Vector(1, 2)
v5 = Vector(3, 4, 5, 6)
v6 = v4 + v5
print(v6)  # Vector(4, 6, 5, 6)

print(-v1)      # Vector(-1, -2, -3)
print(abs(v1))  # 3.7416573867739413
print(~Vector(3, -4)) # Vector(-4, 3)

# __radd__ 예시
v7 = Vector(1, 2, 3)
v8 = Vector(4, 5, 6)
print(v7 + v8)  # Vector(5, 7, 9)
print(v8 + v7)  # Vector(5, 7, 9)

# 스칼라 곱 예시
v9 = Vector(1, 2, 3)
v10 = v9 * 3
print(v10)  # Vector(3, 6, 9)
v11 = 3 * v9
print(v11)  # Vector(3, 6, 9)

이제 mulrmul 메서드는 other가 numbers.Real의 인스턴스인지 확인하여 스칼라 곱셈을 수행한다. numbers.Real은 실수 타입을 포함하는 추상 베이스 클래스이다. 이로 인해 int 및 float 타입이 모두 검사된다.

 

numbers.Real

numbers.Real은 파이썬의 numbers 모듈에서 정의된 추상 베이스 클래스(Abstract Base Class, ABC) 중 하나이다. 이 클래스는 모든 실수형 숫자 타입을 위한 기본 클래스이다. numbers.Real은 실수를 표현하는 데 사용되는 클래스들이 상속받는 인터페이스를 제공한다.

numbers 모듈은 파이썬의 내장 숫자 타입들을 더 잘 구조화하고, 숫자 타입들 사이의 관계를 정의하기 위해 도입되었다. numbers 모듈은 여러 추상 베이스 클래스를 포함하고 있으며, 그 중 Real 클래스는 실수를 표현하는 모든 숫자 타입의 상위 클래스이다.

numbers.Real의 주요 특징:

  1. 추상 베이스 클래스(ABC):
    • numbers.Real은 추상 베이스 클래스이다. 추상 베이스 클래스는 일부 또는 모든 메서드가 구현되지 않은 클래스를 말한다. 파이썬에서 추상 베이스 클래스는 abc 모듈의 ABCMeta 메타클래스를 사용하여 정의된다.
    • 다른 클래스들이 numbers.Real을 상속받아야 하며, 이 클래스에 정의된 메서드를 구현해야 한다.
  2. 상속 계층 구조:
    • numbers.Real은 numbers.Rational의 서브클래스이다. 따라서 모든 Real 숫자는 유리수(Rational)이다.
    • numbers.Real을 상속받는 내장 타입은 float와 fractions.Fraction 등이 있다.
  3. 타입 검사:
    • numbers.Real을 사용하여 주어진 객체가 실수인지 검사할 수 있다. 이는 isinstance() 함수를 사용하여 가능하다.
    • 예를 들어, isinstance(3.14, numbers.Real)은 True를 반환한다.
import numbers

# 실수 검사 예시
print(isinstance(3.14, numbers.Real))  # True
print(isinstance(2, numbers.Real))     # True (정수는 실수의 하위 집합)
print(isinstance(1 + 2j, numbers.Real))  # False (복소수는 실수가 아님)
print(isinstance(3.0, numbers.Real))   # True (float 타입)

위 예제에서 3.14와 2는 numbers.Real의 인스턴스이므로 True를 반환한다. 그러나 1 + 2j (복소수)는 Real이 아니므로 False를 반환한다. 3.0은 float 타입이므로 True를 반환한다.

따라서 numbers.Real은 실수 타입의 객체를 다룰 때 유용하게 사용될 수 있으며, 이를 통해 다양한 숫자 타입을 효과적으로 관리하고 검사할 수 있다.

 

== 연산자 오버로딩

벡터 클래스에 == 연산자를 오버로딩하여 두 벡터가 같은지 비교할 수 있도록 하겠다. 두 벡터가 같은 경우는 그들의 모든 성분이 동일할 때이다.

import numbers

class Vector:
    def __init__(self, *components):
        self.components = list(components)

    def __neg__(self):
        """부호 변경: -벡터"""
        return Vector(*[-c for c in self.components])

    def __abs__(self):
        """벡터의 절대값 (유클리드 거리)"""
        return sum(c ** 2 for c in self.components) ** 0.5

    def __invert__(self):
        """비트 반전: ~벡터 (정수로 가정)"""
        return Vector(*[~c for c in self.components])

    def __add__(self, other):
        """벡터 덧셈: 길이가 다른 벡터도 더할 수 있도록 구현"""
        if isinstance(other, Vector):
            max_len = max(len(self.components), len(other.components))
            new_components = [
                (self.components[i] if i < len(self.components) else 0) +
                (other.components[i] if i < len(other.components) else 0)
                for i in range(max_len)
            ]
            return Vector(*new_components)
        else:
            # 다른 타입의 객체와 덧셈
            return NotImplemented

    def __radd__(self, other):
        """벡터 덧셈: other + 벡터"""
        return self.__add__(other)

    def __mul__(self, other):
        """스칼라 곱: 벡터 * 스칼라"""
        if isinstance(other, numbers.Real):
            new_components = [c * other for c in self.components]
            return Vector(*new_components)
        else:
            return NotImplemented

    def __rmul__(self, other):
        """스칼라 곱: 스칼라 * 벡터"""
        return self.__mul__(other)

    def __eq__(self, other):
        """벡터 비교: 벡터 == 벡터"""
        if isinstance(other, Vector):
            return self.components == other.components
        else:
            return NotImplemented

    def __repr__(self):
        return f"Vector({', '.join(map(str, self.components))})"

# 사용 예시
v1 = Vector(1, 2, 3)
v2 = Vector(4, 5)
v3 = v1 + v2
print(v3)  # Vector(5, 7, 3)

v4 = Vector(1, 2)
v5 = Vector(3, 4, 5, 6)
v6 = v4 + v5
print(v6)  # Vector(4, 6, 5, 6)

print(-v1)      # Vector(-1, -2, -3)
print(abs(v1))  # 3.7416573867739413
print(~Vector(3, -4)) # Vector(-4, 3)

# __radd__ 예시
v7 = Vector(1, 2, 3)
v8 = Vector(4, 5, 6)
print(v7 + v8)  # Vector(5, 7, 9)
print(v8 + v7)  # Vector(5, 7, 9)

# 스칼라 곱 예시
v9 = Vector(1, 2, 3)
v10 = v9 * 3
print(v10)  # Vector(3, 6, 9)
v11 = 3 * v9
print(v11)  # Vector(3, 6, 9)

# 벡터 비교 예시
v12 = Vector(1, 2, 3)
v13 = Vector(1, 2, 3)
v14 = Vector(4, 5, 6)
print(v12 == v13)  # True
print(v12 == v14)  # False

이제 벡터 클래스는 eq 메서드를 포함하고 있어서, 두 벡터가 같은지 비교할 수 있다. 두 벡터가 동일한 경우는 그들의 모든 성분이 동일할 때이다. 예를 들어, v12 == v13은 True를 반환하고, v12 == v14는 False를 반환한다.

 

!= 연산자 오버로딩

== 연산자를 이용하여 != 연산자도 오버로딩하겠다. 이는 ne 메서드를 정의하여 == 연산의 반대 결과를 반환하도록 한다.

import numbers

class Vector:
    def __init__(self, *components):
        self.components = list(components)

    def __neg__(self):
        """부호 변경: -벡터"""
        return Vector(*[-c for c in self.components])

    def __abs__(self):
        """벡터의 절대값 (유클리드 거리)"""
        return sum(c ** 2 for c in self.components) ** 0.5

    def __invert__(self):
        """비트 반전: ~벡터 (정수로 가정)"""
        return Vector(*[~c for c in self.components])

    def __add__(self, other):
        """벡터 덧셈: 길이가 다른 벡터도 더할 수 있도록 구현"""
        if isinstance(other, Vector):
            max_len = max(len(self.components), len(other.components))
            new_components = [
                (self.components[i] if i < len(self.components) else 0) +
                (other.components[i] if i < len(other.components) else 0)
                for i in range(max_len)
            ]
            return Vector(*new_components)
        else:
            # 다른 타입의 객체와 덧셈
            return NotImplemented

    def __radd__(self, other):
        """벡터 덧셈: other + 벡터"""
        return self.__add__(other)

    def __mul__(self, other):
        """스칼라 곱: 벡터 * 스칼라"""
        if isinstance(other, numbers.Real):
            new_components = [c * other for c in self.components]
            return Vector(*new_components)
        else:
            return NotImplemented

    def __rmul__(self, other):
        """스칼라 곱: 스칼라 * 벡터"""
        return self.__mul__(other)

    def __eq__(self, other):
        """벡터 비교: 벡터 == 벡터"""
        if isinstance(other, Vector):
            return self.components == other.components
        else:
            return NotImplemented

    def __ne__(self, other):
        """벡터 비교: 벡터 != 벡터"""
        if isinstance(other, Vector):
            return not self.__eq__(other)
        else:
            return NotImplemented

    def __repr__(self):
        return f"Vector({', '.join(map(str, self.components))})"

# 사용 예시
v1 = Vector(1, 2, 3)
v2 = Vector(4, 5)
v3 = v1 + v2
print(v3)  # Vector(5, 7, 3)

v4 = Vector(1, 2)
v5 = Vector(3, 4, 5, 6)
v6 = v4 + v5
print(v6)  # Vector(4, 6, 5, 6)

print(-v1)      # Vector(-1, -2, -3)
print(abs(v1))  # 3.7416573867739413
print(~Vector(3, -4)) # Vector(-4, 3)

# __radd__ 예시
v7 = Vector(1, 2, 3)
v8 = Vector(4, 5, 6)
print(v7 + v8)  # Vector(5, 7, 9)
print(v8 + v7)  # Vector(5, 7, 9)

# 스칼라 곱 예시
v9 = Vector(1, 2, 3)
v10 = v9 * 3
print(v10)  # Vector(3, 6, 9)
v11 = 3 * v9
print(v11)  # Vector(3, 6, 9)

# 벡터 비교 예시
v12 = Vector(1, 2, 3)
v13 = Vector(1, 2, 3)
v14 = Vector(4, 5, 6)
print(v12 == v13)  # True
print(v12 == v14)  # False
print(v12 != v13)  # False
print(v12 != v14)  # True

이제 ne 메서드도 포함되어 있어 != 연산자를 사용하여 두 벡터가 다른지 비교할 수 있다. __ne__는 __eq__의 결과를 반대로 반환한다. 예를 들어, v12 != v13은 False를 반환하고, v12 != v14는 True를 반환다.

 

모든 코드는 github에 저장되어 있습니다.

https://github.com/SeongUk18/python

728x90

'Python study' 카테고리의 다른 글

콘텍스트 관리자와 else 블록  (0) 2024.06.04
제너레이터 (Generator)  (1) 2024.05.29
다중 상속 (Multiple inheritance)  (0) 2024.05.28
인터페이스  (0) 2024.05.26
시퀀스 해킹, 해시, 슬라이스  (0) 2024.05.22