9.1. numbers
— 숫자 추상 베이스 클래스¶
버전 2.6에 추가.
numbers
모듈(PEP 3141)은 숫자에 대한 추상 베이스 클래스 의 계층 구조를 정의합니다. 계층 구조가 깊어질수록 더 많은 연산이 정의되어 있습니다. 이 모듈에 정의된 모든 형은 인스턴스로 만들 수 없습니다.
-
class
numbers.
Number
¶ 숫자 계층의 최상위 클래스입니다. 형에 상관없이 인자 x 가 숫자인지 확인하려면
isinstance(x, Number)
를 사용하세요.
9.1.1. 숫자 계층¶
-
class
numbers.
Complex
¶ 이 서브 클래스는 복소수를 표현하고 내장
complex
형에 사용되는 연산을 포함합니다. 여기에는complex
와bool
형으로의 변환과real
,imag
,+
,-
,*
,/
,abs()
,conjugate()
,==
,!=
이 포함됩니다.-
와!=
를 제외하고는 모두 추상입니다.-
real
¶ 추상. 복소수의 실수부를 반환합니다.
-
imag
¶ 추상. 복소수의 허수부를 반환합니다.
-
conjugate
()¶ 추상 메서드. 켤레 복소수를 반환합니다. 예를 들어
(1+3j).conjugate() == (1-3j)
입니다.
-
-
class
numbers.
Real
¶ Real
클래스는Complex
클래스에 실수 연산을 추가합니다.요약하면
float
로의 변환과math.trunc()
,round()
,math.floor()
,math.ceil()
,divmod()
,//
,%
,<
,<=
,>
,>=
가 포함됩니다.이 클래스는 또한
complex()
,real
,imag
,conjugate()
를 위한 기본값을 제공합니다.
9.1.2. 형 구현을 위한 주의 사항¶
구현자는 동일한 숫자가 같게 취급되고 같은 값으로 해싱되도록 해야 합니다. 만약 종류가 다른 실수의 하위 형이 있는 경우 조금 까다로울 수 있습니다. 예를 들어 fractions.Fraction
클래스는 hash()
함수를 다음과 같이 구현합니다:
def __hash__(self):
if self.denominator == 1:
# Get integers right.
return hash(self.numerator)
# Expensive check, but definitely correct.
if self == float(self):
return hash(float(self))
else:
# Use tuple's hash to avoid a high collision rate on
# simple fractions.
return hash((self.numerator, self.denominator))
9.1.2.1. 더 많은 숫자 추상 베이스 클래스(ABC) 추가¶
물론 숫자를 위한 ABC를 추가하는 것이 가능합니다. 그렇지 않으면 엉망으로 상속 계층이 구현될 것입니다. Complex
와 Real
사이에 다음과 같이 MyFoo
를 추가할 수 있습니다:
class MyFoo(Complex): ...
MyFoo.register(Real)
9.1.2.2. 산술 연산 구현¶
다른 형에 대한 연산은 두 인자의 형에 관해 알고 있는 구현을 호출하거나 두 인자를 가장 비슷한 내장형으로 변환하여 연산하도록 산술 연산을 구현하는 것이 좋습니다. Integral
클래스의 하위 형일 경우에 __add__()
와 __radd__()
메서드는 다음과 같이 정의되어야 함을 의미합니다:
class MyIntegral(Integral):
def __add__(self, other):
if isinstance(other, MyIntegral):
return do_my_adding_stuff(self, other)
elif isinstance(other, OtherTypeIKnowAbout):
return do_my_other_adding_stuff(self, other)
else:
return NotImplemented
def __radd__(self, other):
if isinstance(other, MyIntegral):
return do_my_adding_stuff(other, self)
elif isinstance(other, OtherTypeIKnowAbout):
return do_my_other_adding_stuff(other, self)
elif isinstance(other, Integral):
return int(other) + int(self)
elif isinstance(other, Real):
return float(other) + float(self)
elif isinstance(other, Complex):
return complex(other) + complex(self)
else:
return NotImplemented
Complex
클래스의 서브클래스에는 다섯 가지의 서로 다른 혼합형 연산이 있습니다. 위의 코드에서 MyIntegral
와 OtherTypeIKnowAbout
를 제외한 나머지를 기본구조라고 하겠습니다. a
는 Complex
의 하위 형인 A
의 인스턴스입니다(즉 a : A <: Complex
입니다). 비슷하게 b : B <: Complex
입니다. a + b
인 경우를 생각해 보겠습니다:
만약
A
가b
를 받는__add__()
메서드를 정의했다면 모든 것이 문제없이 처리됩니다.
A
가 기본구조 코드로 진입하고__add__()
로 부터 어떤 값을 반환한다면B
가 똑똑하게 정의한__radd__()
메서드를 놓칠 수 있습니다. 이를 피하려면 기본구조는__add__()
에서NotImplemented
를 반환해야 합니다. (또는A
가__add__()
메서드를 전혀 구현하지 않을 수도 있습니다.)그다음
B
의__radd__()
메서드가 기회를 얻습니다. 이 메서드가a
를 받을 수 있다면 모든 것이 문제없이 처리됩니다.기본구조 코드로 돌아온다면 더 시도해 볼 수 있는 메서드가 없으므로 기본적으로 수행될 구현을 작성해야 합니다.
만약
B <: A
라면 파이썬은A.__add__
메서드 전에B.__radd__
를 시도합니다.A
에 대해서 알고B
가 구현되었기 때문에 이런 행동은 문제없습니다. 따라서Complex
에 위임하기 전에 이 인스턴스를 처리할 수 있습니다.
만약 어떤 것도 공유하지 않는 A <: Complex
와 B <: Real
라면 적절한 공유 연산(shared operation)은 내장 complex
클래스에 연관된 것입니다. 양쪽의 __radd__()
메서드가 여기에 해당하므로 a+b == b+a
가 됩니다.
대부분 주어진 어떤 형에 대한 연산은 매우 비슷하므로, 주어진 연산자의 정방향(forward) 인스턴스와 역방향(reverse) 인스턴스를 생성하는 헬퍼 함수를 정의하는 것이 유용합니다. 예를 들어 fractions.Fraction
클래스는 다음과 같이 사용합니다:
def _operator_fallbacks(monomorphic_operator, fallback_operator):
def forward(a, b):
if isinstance(b, (int, long, Fraction)):
return monomorphic_operator(a, b)
elif isinstance(b, float):
return fallback_operator(float(a), b)
elif isinstance(b, complex):
return fallback_operator(complex(a), b)
else:
return NotImplemented
forward.__name__ = '__' + fallback_operator.__name__ + '__'
forward.__doc__ = monomorphic_operator.__doc__
def reverse(b, a):
if isinstance(a, Rational):
# Includes ints.
return monomorphic_operator(a, b)
elif isinstance(a, numbers.Real):
return fallback_operator(float(a), float(b))
elif isinstance(a, numbers.Complex):
return fallback_operator(complex(a), complex(b))
else:
return NotImplemented
reverse.__name__ = '__r' + fallback_operator.__name__ + '__'
reverse.__doc__ = monomorphic_operator.__doc__
return forward, reverse
def _add(a, b):
"""a + b"""
return Fraction(a.numerator * b.denominator +
b.numerator * a.denominator,
a.denominator * b.denominator)
__add__, __radd__ = _operator_fallbacks(_add, operator.add)
# ...