summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGuido van Rossum <guido@python.org>2007-08-30 17:45:54 (GMT)
committerGuido van Rossum <guido@python.org>2007-08-30 17:45:54 (GMT)
commit1daf954dcf6fa5630b049f1d1edcc9cc656ea5be (patch)
tree95de5f7d39e32bfb755eba5988c92f366f714782
parent3cd6537beb7df1831fcd7cfdc6d74616648feddf (diff)
downloadcpython-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.py325
-rw-r--r--Lib/test/test_abstract_numbers.py53
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()