단항 연산자 오버로딩
벡터(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)
이 벡터 클래스에는 세 가지 단항 연산자가 오버로딩되어 있다:
- neg: 벡터의 부호를 반전한다.
- abs: 벡터의 절대값(유클리드 거리)을 계산한다.
- 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 모두 정상적으로 작동한다.
* 연산자 오버로딩
벡터 클래스에 스칼라와의 곱셈을 위한 * 연산자도 오버로딩해보겠다. 이를 위해 mul 및 rmul 메서드를 추가하여 스칼라와 벡터의 곱셈을 처리할 수 있도록 한다. 또한 스칼라가 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)
이제 mul 및 rmul 메서드는 other가 numbers.Real의 인스턴스인지 확인하여 스칼라 곱셈을 수행한다. numbers.Real은 실수 타입을 포함하는 추상 베이스 클래스이다. 이로 인해 int 및 float 타입이 모두 검사된다.
numbers.Real
numbers.Real은 파이썬의 numbers 모듈에서 정의된 추상 베이스 클래스(Abstract Base Class, ABC) 중 하나이다. 이 클래스는 모든 실수형 숫자 타입을 위한 기본 클래스이다. numbers.Real은 실수를 표현하는 데 사용되는 클래스들이 상속받는 인터페이스를 제공한다.
numbers 모듈은 파이썬의 내장 숫자 타입들을 더 잘 구조화하고, 숫자 타입들 사이의 관계를 정의하기 위해 도입되었다. numbers 모듈은 여러 추상 베이스 클래스를 포함하고 있으며, 그 중 Real 클래스는 실수를 표현하는 모든 숫자 타입의 상위 클래스이다.
numbers.Real의 주요 특징:
- 추상 베이스 클래스(ABC):
- numbers.Real은 추상 베이스 클래스이다. 추상 베이스 클래스는 일부 또는 모든 메서드가 구현되지 않은 클래스를 말한다. 파이썬에서 추상 베이스 클래스는 abc 모듈의 ABCMeta 메타클래스를 사용하여 정의된다.
- 다른 클래스들이 numbers.Real을 상속받아야 하며, 이 클래스에 정의된 메서드를 구현해야 한다.
- 상속 계층 구조:
- numbers.Real은 numbers.Rational의 서브클래스이다. 따라서 모든 Real 숫자는 유리수(Rational)이다.
- numbers.Real을 상속받는 내장 타입은 float와 fractions.Fraction 등이 있다.
- 타입 검사:
- 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에 저장되어 있습니다.
'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 |