diff options
author | Guido van Rossum <guido@python.org> | 2007-08-30 17:45:54 (GMT) |
---|---|---|
committer | Guido van Rossum <guido@python.org> | 2007-08-30 17:45:54 (GMT) |
commit | 1daf954dcf6fa5630b049f1d1edcc9cc656ea5be (patch) | |
tree | 95de5f7d39e32bfb755eba5988c92f366f714782 | |
parent | 3cd6537beb7df1831fcd7cfdc6d74616648feddf (diff) | |
download | cpython-1daf954dcf6fa5630b049f1d1edcc9cc656ea5be.zip cpython-1daf954dcf6fa5630b049f1d1edcc9cc656ea5be.tar.gz cpython-1daf954dcf6fa5630b049f1d1edcc9cc656ea5be.tar.bz2 |
Add numbers.py. I suspect this is an old version, but Jeffrey is out
of town, and it will have to do for now.
-rw-r--r-- | Lib/numbers.py | 325 | ||||
-rw-r--r-- | Lib/test/test_abstract_numbers.py | 53 |
2 files changed, 378 insertions, 0 deletions
diff --git a/Lib/numbers.py b/Lib/numbers.py new file mode 100644 index 0000000..8678a3a --- /dev/null +++ b/Lib/numbers.py @@ -0,0 +1,325 @@ +# Copyright 2007 Google, Inc. All Rights Reserved. +# Licensed to PSF under a Contributor Agreement. + +"""Abstract Base Classes (ABCs) for numbers, according to PEP 3141.""" + +from abc import ABCMeta, abstractmethod, abstractproperty + +__all__ = ["Number", "Exact", "Inexact", + "Complex", "Real", "Rational", "Integral", + ] + + +class Number(metaclass=ABCMeta): + """All numbers inherit from this class. + + If you just want to check if an argument x is a number, without + caring what kind, use isinstance(x, Number). + """ + + +class Exact(Number): + """Operations on instances of this type are exact. + + As long as the result of a homogenous operation is of the same + type, you can assume that it was computed exactly, and there are + no round-off errors. Laws like commutativity and associativity + hold. + """ + +Exact.register(int) + + +class Inexact(Number): + """Operations on instances of this type are inexact. + + Given X, an instance of Inexact, it is possible that (X + -X) + 3 + == 3, but X + (-X + 3) == 0. The exact form this error takes will + vary by type, but it's generally unsafe to compare this type for + equality. + """ + +Inexact.register(complex) +Inexact.register(float) + + +class Complex(Number): + """Complex defines the operations that work on the builtin complex type. + + In short, those are: a conversion to complex, .real, .imag, +, -, + *, /, abs(), .conjugate, ==, and !=. + + If it is given heterogenous arguments, and doesn't have special + knowledge about them, it should fall back to the builtin complex + type as described below. + """ + + @abstractmethod + def __complex__(self): + """Return a builtin complex instance.""" + + def __bool__(self): + """True if self != 0.""" + return self != 0 + + @abstractproperty + def real(self): + """Retrieve the real component of this number. + + This should subclass Real. + """ + raise NotImplementedError + + @abstractproperty + def imag(self): + """Retrieve the real component of this number. + + This should subclass Real. + """ + raise NotImplementedError + + @abstractmethod + def __add__(self, other): + raise NotImplementedError + + @abstractmethod + def __radd__(self, other): + raise NotImplementedError + + @abstractmethod + def __neg__(self): + raise NotImplementedError + + def __pos__(self): + return self + + def __sub__(self, other): + return self + -other + + def __rsub__(self, other): + return -self + other + + @abstractmethod + def __mul__(self, other): + raise NotImplementedError + + @abstractmethod + def __rmul__(self, other): + raise NotImplementedError + + @abstractmethod + def __div__(self, other): + raise NotImplementedError + + @abstractmethod + def __rdiv__(self, other): + raise NotImplementedError + + @abstractmethod + def __pow__(self, exponent): + """Like division, a**b should promote to complex when necessary.""" + raise NotImplementedError + + @abstractmethod + def __rpow__(self, base): + raise NotImplementedError + + @abstractmethod + def __abs__(self): + """Returns the Real distance from 0.""" + raise NotImplementedError + + @abstractmethod + def conjugate(self): + """(x+y*i).conjugate() returns (x-y*i).""" + raise NotImplementedError + + @abstractmethod + def __eq__(self, other): + raise NotImplementedError + + def __ne__(self, other): + return not (self == other) + +Complex.register(complex) + + +class Real(Complex): + """To Complex, Real adds the operations that work on real numbers. + + In short, those are: a conversion to float, trunc(), divmod, + %, <, <=, >, and >=. + + Real also provides defaults for the derived operations. + """ + + @abstractmethod + def __float__(self): + """Any Real can be converted to a native float object.""" + raise NotImplementedError + + @abstractmethod + def __trunc__(self): + """Truncates self to an Integral. + + Returns an Integral i such that: + * i>0 iff self>0 + * abs(i) <= abs(self). + """ + raise NotImplementedError + + def __divmod__(self, other): + """The pair (self // other, self % other). + + Sometimes this can be computed faster than the pair of + operations. + """ + return (self // other, self % other) + + def __rdivmod__(self, other): + """The pair (self // other, self % other). + + Sometimes this can be computed faster than the pair of + operations. + """ + return (other // self, other % self) + + @abstractmethod + def __floordiv__(self, other): + """The floor() of self/other.""" + raise NotImplementedError + + @abstractmethod + def __rfloordiv__(self, other): + """The floor() of other/self.""" + raise NotImplementedError + + @abstractmethod + def __mod__(self, other): + raise NotImplementedError + + @abstractmethod + def __rmod__(self, other): + raise NotImplementedError + + @abstractmethod + def __lt__(self, other): + """< on Reals defines a total ordering, except perhaps for NaN.""" + raise NotImplementedError + + def __le__(self, other): + raise NotImplementedError + + # Concrete implementations of Complex abstract methods. + def __complex__(self): + return complex(float(self)) + + @property + def real(self): + return self + + @property + def imag(self): + return 0 + + def conjugate(self): + """Conjugate is a no-op for Reals.""" + return self + +Real.register(float) + + +class Rational(Real, Exact): + """.numerator and .denominator should be in lowest terms.""" + + @abstractproperty + def numerator(self): + raise NotImplementedError + + @abstractproperty + def denominator(self): + raise NotImplementedError + + # Concrete implementation of Real's conversion to float. + def __float__(self): + return self.numerator / self.denominator + + +class Integral(Rational): + """Integral adds a conversion to int and the bit-string operations.""" + + @abstractmethod + def __int__(self): + raise NotImplementedError + + def __index__(self): + return int(self) + + @abstractmethod + def __pow__(self, exponent, modulus): + """self ** exponent % modulus, but maybe faster. + + Implement this if you want to support the 3-argument version + of pow(). Otherwise, just implement the 2-argument version + described in Complex. Raise a TypeError if exponent < 0 or any + argument isn't Integral. + """ + raise NotImplementedError + + @abstractmethod + def __lshift__(self, other): + raise NotImplementedError + + @abstractmethod + def __rlshift__(self, other): + raise NotImplementedError + + @abstractmethod + def __rshift__(self, other): + raise NotImplementedError + + @abstractmethod + def __rrshift__(self, other): + raise NotImplementedError + + @abstractmethod + def __and__(self, other): + raise NotImplementedError + + @abstractmethod + def __rand__(self, other): + raise NotImplementedError + + @abstractmethod + def __xor__(self, other): + raise NotImplementedError + + @abstractmethod + def __rxor__(self, other): + raise NotImplementedError + + @abstractmethod + def __or__(self, other): + raise NotImplementedError + + @abstractmethod + def __ror__(self, other): + raise NotImplementedError + + @abstractmethod + def __invert__(self): + raise NotImplementedError + + # Concrete implementations of Rational and Real abstract methods. + def __float__(self): + return float(int(self)) + + @property + def numerator(self): + return self + + @property + def denominator(self): + return 1 + +Integral.register(int) diff --git a/Lib/test/test_abstract_numbers.py b/Lib/test/test_abstract_numbers.py new file mode 100644 index 0000000..9fffd28 --- /dev/null +++ b/Lib/test/test_abstract_numbers.py @@ -0,0 +1,53 @@ +"""Unit tests for numbers.py.""" + +import unittest +from test import test_support +from numbers import Number +from numbers import Exact, Inexact +from numbers import Complex, Real, Rational, Integral +import operator + +class TestNumbers(unittest.TestCase): + def test_int(self): + self.failUnless(issubclass(int, Integral)) + self.failUnless(issubclass(int, Complex)) + self.failUnless(issubclass(int, Exact)) + self.failIf(issubclass(int, Inexact)) + + self.assertEqual(7, int(7).real) + self.assertEqual(0, int(7).imag) + self.assertEqual(7, int(7).conjugate()) + self.assertEqual(7, int(7).numerator) + self.assertEqual(1, int(7).denominator) + + def test_float(self): + self.failIf(issubclass(float, Rational)) + self.failUnless(issubclass(float, Real)) + self.failIf(issubclass(float, Exact)) + self.failUnless(issubclass(float, Inexact)) + + self.assertEqual(7.3, float(7.3).real) + self.assertEqual(0, float(7.3).imag) + self.assertEqual(7.3, float(7.3).conjugate()) + + def test_complex(self): + self.failIf(issubclass(complex, Real)) + self.failUnless(issubclass(complex, Complex)) + self.failIf(issubclass(complex, Exact)) + self.failUnless(issubclass(complex, Inexact)) + + c1, c2 = complex(3, 2), complex(4,1) + # TODO: Uncomment this test when trunc() exists. + #self.assertRaises(None, trunc, c1) + self.assertRaises(TypeError, operator.mod, c1, c2) + self.assertRaises(TypeError, divmod, c1, c2) + self.assertRaises(TypeError, operator.floordiv, c1, c2) + self.assertRaises(TypeError, float, c1) + self.assertRaises(TypeError, int, c1) + +def test_main(): + test_support.run_unittest(TestNumbers) + + +if __name__ == "__main__": + unittest.main() |