numbers — 숫자 추상 베이스 클래스
numbers — 숫자 추상 베이스 클래스
numbers 모듈(PEP 3141)은 점진적으로 더 많은 연산을 정의하는 숫자 추상 베이스 클래스(ABC) 계층을 정의해요. 이 모듈에서 정의된 어떤 타입도 인스턴스화하도록 의도되지 않았어요.
출처: Python 표준 라이브러리
본문
class numbers.Number
숫자 계층의 뿌리예요. 인자 x가 숫자인지만 확인하고 싶고 종류는 상관없다면 isinstance(x, Number)를 쓰면 돼요.
숫자 타워 (The numeric tower)
class numbers.Complex
이 타입의 하위 클래스는 복소수를 나타내며 내장 complex 타입에서 동작하는 연산을 포함해요. 그것들은: complex와 bool로의 변환, real, imag, +, -, *, /, **, abs(), conjugate(), ==, !=예요. -와 !=를 제외한 모두가 추상이에요.
real— 추상. 이 숫자의 실수 성분을 가져와요.imag— 추상. 이 숫자의 허수 성분을 가져와요.abstractmethod conjugate()— 추상. 복소 켤레를 돌려줘요. 예:(1+3j).conjugate() == (1-3j).
class numbers.Real
Complex에 대해, Real은 실수에서 동작하는 연산을 추가해요. 간단히 말해: float로의 변환, math.trunc(), round(), math.floor(), math.ceil(), divmod(), //, %, <, <=, >, >=예요. Real은 또한 complex(), real, imag, conjugate()에 대한 기본값도 제공해요.
class numbers.Rational
Real의 하위 타입이고 numerator와 denominator 속성을 추가해요. float()에 대한 기본값도 제공해요. numerator와 denominator 값은 Integral의 인스턴스이고, 분모가 양수인 기약분수(lowest terms)여야 해요.
numerator— 추상. 이 유리수의 분자.denominator— 추상. 이 유리수의 분모.
class numbers.Integral
Rational의 하위 타입이고 int로의 변환을 추가해요. float(), numerator, denominator에 대한 기본값을 제공해요. 나머지를 포함한 pow()와 비트 문자열 연산 <<, >>, &, ^, |, ~에 대한 추상 메서드를 추가해요.
타입 구현자를 위한 참고
구현자는 같은 수를 같게 만들고 같은 값으로 해시해야 한다는 점에 주의해야 해요. 실수의 두 가지 다른 확장이 있다면 이는 미묘할 수 있어요. "숫자 타입의 해싱(Hashing of numeric types)"도 참고하세요.
더 많은 숫자 ABC 추가하기
물론 숫자에 대한 ABC가 더 있을 수 있고, 그것을 추가할 가능성을 배제한다면 빈약한 계층이 돼요. Complex와 Real 사이에 MyFoo를 다음과 같이 추가할 수 있어요:
class MyFoo(Complex): ...
MyFoo.register(Real)
산술 연산 구현하기
혼합 모드 연산이 두 인자의 타입을 모두 아는 구현을 호출하거나, 둘 다 가장 가까운 내장 타입으로 변환해 거기서 연산하도록 산술 연산을 구현하고 싶어요. 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 하위 클래스에서의 혼합 타입 연산에는 5가지 다른 경우가 있어요. 위 코드에서 MyIntegral과 OtherTypeIKnowAbout을 언급하지 않는 부분을 모두 "boilerplate"라고 부를게요. a는 Complex의 하위 타입인 A의 인스턴스(a : A <: Complex)이고, b : B <: Complex예요. a + b를 생각해 보죠:
A가b를 받아들이는__add__()를 정의하면, 모든 게 잘 돼요.A가 boilerplate 코드로 빠지고__add__()에서 값을 돌려주면,B가 더 똑똑한__radd__()를 정의했을 가능성을 놓치게 돼요. 그래서 boilerplate는__add__()에서NotImplemented를 돌려줘야 해요. (또는A가__add__()를 아예 구현하지 않을 수도 있어요.)- 그러면
B의__radd__()가 기회를 얻어요.a를 받아들이면 모든 게 잘 돼요. - 그것도 boilerplate로 빠지면, 시도할 수 있는 메서드가 더 없으므로 여기가 기본 구현이 있어야 할 자리예요.
B <: A이면, Python은A.__add__보다B.__radd__를 먼저 시도해요.A에 대한 지식으로 구현됐으니,Complex에 위임하기 전에 그 인스턴스를 처리할 수 있으므로 괜찮아요.A <: Complex와B <: Real이 다른 지식을 공유하지 않는다면, 적절한 공유 연산은 내장complex를 포함하는 것이고, 두__radd__()가 거기로 모여서a+b == b+a가 돼요.
주어진 타입의 대부분 연산은 매우 비슷하므로, 주어진 연산자의 순방향·역방향 인스턴스를 생성하는 헬퍼 함수를 정의하는 게 유용할 수 있어요. 예를 들어 fractions.Fraction은 이렇게 사용해요:
def _operator_fallbacks(monomorphic_operator, fallback_operator):
def forward(a, b):
if isinstance(b, (int, 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, Real):
return fallback_operator(float(a), float(b))
elif isinstance(a, 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)
# ...
더 알아보기
math— 수학 함수.fractions,decimal— numbers ABC를 활용하는 구체적 숫자 타입.