| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
1 parent 3cd6537 commit 1daf954
2 files changed
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -0,0 +1,325 @@ | |||
| 1 | + # Copyright 2007 Google, Inc. All Rights Reserved. | ||
| 2 | + # Licensed to PSF under a Contributor Agreement. | ||
| 3 | + | ||
| 4 | + """Abstract Base Classes (ABCs) for numbers, according to PEP 3141.""" | ||
| 5 | + | ||
| 6 | + from abc import ABCMeta, abstractmethod, abstractproperty | ||
| 7 | + | ||
| 8 | + __all__ = ["Number", "Exact", "Inexact", | ||
| 9 | + "Complex", "Real", "Rational", "Integral", | ||
| 10 | + ] | ||
| 11 | + | ||
| 12 | + | ||
| 13 | + class Number(metaclass=ABCMeta): | ||
| 14 | + """All numbers inherit from this class. | ||
| 15 | + | ||
| 16 | + If you just want to check if an argument x is a number, without | ||
| 17 | + caring what kind, use isinstance(x, Number). | ||
| 18 | + """ | ||
| 19 | + | ||
| 20 | + | ||
| 21 | + class Exact(Number): | ||
| 22 | + """Operations on instances of this type are exact. | ||
| 23 | + | ||
| 24 | + As long as the result of a homogenous operation is of the same | ||
| 25 | + type, you can assume that it was computed exactly, and there are | ||
| 26 | + no round-off errors. Laws like commutativity and associativity | ||
| 27 | + hold. | ||
| 28 | + """ | ||
| 29 | + | ||
| 30 | + Exact.register(int) | ||
| 31 | + | ||
| 32 | + | ||
| 33 | + class Inexact(Number): | ||
| 34 | + """Operations on instances of this type are inexact. | ||
| 35 | + | ||
| 36 | + Given X, an instance of Inexact, it is possible that (X + -X) + 3 | ||
| 37 | + == 3, but X + (-X + 3) == 0. The exact form this error takes will | ||
| 38 | + vary by type, but it's generally unsafe to compare this type for | ||
| 39 | + equality. | ||
| 40 | + """ | ||
| 41 | + | ||
| 42 | + Inexact.register(complex) | ||
| 43 | + Inexact.register(float) | ||
| 44 | + | ||
| 45 | + | ||
| 46 | + class Complex(Number): | ||
| 47 | + """Complex defines the operations that work on the builtin complex type. | ||
| 48 | + | ||
| 49 | + In short, those are: a conversion to complex, .real, .imag, +, -, | ||
| 50 | + *, /, abs(), .conjugate, ==, and !=. | ||
| 51 | + | ||
| 52 | + If it is given heterogenous arguments, and doesn't have special | ||
| 53 | + knowledge about them, it should fall back to the builtin complex | ||
| 54 | + type as described below. | ||
| 55 | + """ | ||
| 56 | + | ||
| 57 | + @abstractmethod | ||
| 58 | + def __complex__(self): | ||
| 59 | + """Return a builtin complex instance.""" | ||
| 60 | + | ||
| 61 | + def __bool__(self): | ||
| 62 | + """True if self != 0.""" | ||
| 63 | + return self != 0 | ||
| 64 | + | ||
| 65 | + @abstractproperty | ||
| 66 | + def real(self): | ||
| 67 | + """Retrieve the real component of this number. | ||
| 68 | + | ||
| 69 | + This should subclass Real. | ||
| 70 | + """ | ||
| 71 | + raise NotImplementedError | ||
| 72 | + | ||
| 73 | + @abstractproperty | ||
| 74 | + def imag(self): | ||
| 75 | + """Retrieve the real component of this number. | ||
| 76 | + | ||
| 77 | + This should subclass Real. | ||
| 78 | + """ | ||
| 79 | + raise NotImplementedError | ||
| 80 | + | ||
| 81 | + @abstractmethod | ||
| 82 | + def __add__(self, other): | ||
| 83 | + raise NotImplementedError | ||
| 84 | + | ||
| 85 | + @abstractmethod | ||
| 86 | + def __radd__(self, other): | ||
| 87 | + raise NotImplementedError | ||
| 88 | + | ||
| 89 | + @abstractmethod | ||
| 90 | + def __neg__(self): | ||
| 91 | + raise NotImplementedError | ||
| 92 | + | ||
| 93 | + def __pos__(self): | ||
| 94 | + return self | ||
| 95 | + | ||
| 96 | + def __sub__(self, other): | ||
| 97 | + return self + -other | ||
| 98 | + | ||
| 99 | + def __rsub__(self, other): | ||
| 100 | + return -self + other | ||
| 101 | + | ||
| 102 | + @abstractmethod | ||
| 103 | + def __mul__(self, other): | ||
| 104 | + raise NotImplementedError | ||
| 105 | + | ||
| 106 | + @abstractmethod | ||
| 107 | + def __rmul__(self, other): | ||
| 108 | + raise NotImplementedError | ||
| 109 | + | ||
| 110 | + @abstractmethod | ||
| 111 | + def __div__(self, other): | ||
| 112 | + raise NotImplementedError | ||
| 113 | + | ||
| 114 | + @abstractmethod | ||
| 115 | + def __rdiv__(self, other): | ||
| 116 | + raise NotImplementedError | ||
| 117 | + | ||
| 118 | + @abstractmethod | ||
| 119 | + def __pow__(self, exponent): | ||
| 120 | + """Like division, a**b should promote to complex when necessary.""" | ||
| 121 | + raise NotImplementedError | ||
| 122 | + | ||
| 123 | + @abstractmethod | ||
| 124 | + def __rpow__(self, base): | ||
| 125 | + raise NotImplementedError | ||
| 126 | + | ||
| 127 | + @abstractmethod | ||
| 128 | + def __abs__(self): | ||
| 129 | + """Returns the Real distance from 0.""" | ||
| 130 | + raise NotImplementedError | ||
| 131 | + | ||
| 132 | + @abstractmethod | ||
| 133 | + def conjugate(self): | ||
| 134 | + """(x+y*i).conjugate() returns (x-y*i).""" | ||
| 135 | + raise NotImplementedError | ||
| 136 | + | ||
| 137 | + @abstractmethod | ||
| 138 | + def __eq__(self, other): | ||
| 139 | + raise NotImplementedError | ||
| 140 | + | ||
| 141 | + def __ne__(self, other): | ||
| 142 | + return not (self == other) | ||
| 143 | + | ||
| 144 | + Complex.register(complex) | ||
| 145 | + | ||
| 146 | + | ||
| 147 | + class Real(Complex): | ||
| 148 | + """To Complex, Real adds the operations that work on real numbers. | ||
| 149 | + | ||
| 150 | + In short, those are: a conversion to float, trunc(), divmod, | ||
| 151 | + %, <, <=, >, and >=. | ||
| 152 | + | ||
| 153 | + Real also provides defaults for the derived operations. | ||
| 154 | + """ | ||
| 155 | + | ||
| 156 | + @abstractmethod | ||
| 157 | + def __float__(self): | ||
| 158 | + """Any Real can be converted to a native float object.""" | ||
| 159 | + raise NotImplementedError | ||
| 160 | + | ||
| 161 | + @abstractmethod | ||
| 162 | + def __trunc__(self): | ||
| 163 | + """Truncates self to an Integral. | ||
| 164 | + | ||
| 165 | + Returns an Integral i such that: | ||
| 166 | + * i>0 iff self>0 | ||
| 167 | + * abs(i) <= abs(self). | ||
| 168 | + """ | ||
| 169 | + raise NotImplementedError | ||
| 170 | + | ||
| 171 | + def __divmod__(self, other): | ||
| 172 | + """The pair (self // other, self % other). | ||
| 173 | + | ||
| 174 | + Sometimes this can be computed faster than the pair of | ||
| 175 | + operations. | ||
| 176 | + """ | ||
| 177 | + return (self // other, self % other) | ||
| 178 | + | ||
| 179 | + def __rdivmod__(self, other): | ||
| 180 | + """The pair (self // other, self % other). | ||
| 181 | + | ||
| 182 | + Sometimes this can be computed faster than the pair of | ||
| 183 | + operations. | ||
| 184 | + """ | ||
| 185 | + return (other // self, other % self) | ||
| 186 | + | ||
| 187 | + @abstractmethod | ||
| 188 | + def __floordiv__(self, other): | ||
| 189 | + """The floor() of self/other.""" | ||
| 190 | + raise NotImplementedError | ||
| 191 | + | ||
| 192 | + @abstractmethod | ||
| 193 | + def __rfloordiv__(self, other): | ||
| 194 | + """The floor() of other/self.""" | ||
| 195 | + raise NotImplementedError | ||
| 196 | + | ||
| 197 | + @abstractmethod | ||
| 198 | + def __mod__(self, other): | ||
| 199 | + raise NotImplementedError | ||
| 200 | + | ||
| 201 | + @abstractmethod | ||
| 202 | + def __rmod__(self, other): | ||
| 203 | + raise NotImplementedError | ||
| 204 | + | ||
| 205 | + @abstractmethod | ||
| 206 | + def __lt__(self, other): | ||
| 207 | + """< on Reals defines a total ordering, except perhaps for NaN.""" | ||
| 208 | + raise NotImplementedError | ||
| 209 | + | ||
| 210 | + def __le__(self, other): | ||
| 211 | + raise NotImplementedError | ||
| 212 | + | ||
| 213 | + # Concrete implementations of Complex abstract methods. | ||
| 214 | + def __complex__(self): | ||
| 215 | + return complex(float(self)) | ||
| 216 | + | ||
| 217 | + @property | ||
| 218 | + def real(self): | ||
| 219 | + return self | ||
| 220 | + | ||
| 221 | + @property | ||
| 222 | + def imag(self): | ||
| 223 | + return 0 | ||
| 224 | + | ||
| 225 | + def conjugate(self): | ||
| 226 | + """Conjugate is a no-op for Reals.""" | ||
| 227 | + return self | ||
| 228 | + | ||
| 229 | + Real.register(float) | ||
| 230 | + | ||
| 231 | + | ||
| 232 | + class Rational(Real, Exact): | ||
| 233 | + """.numerator and .denominator should be in lowest terms.""" | ||
| 234 | + | ||
| 235 | + @abstractproperty | ||
| 236 | + def numerator(self): | ||
| 237 | + raise NotImplementedError | ||
| 238 | + | ||
| 239 | + @abstractproperty | ||
| 240 | + def denominator(self): | ||
| 241 | + raise NotImplementedError | ||
| 242 | + | ||
| 243 | + # Concrete implementation of Real's conversion to float. | ||
| 244 | + def __float__(self): | ||
| 245 | + return self.numerator / self.denominator | ||
| 246 | + | ||
| 247 | + | ||
| 248 | + class Integral(Rational): | ||
| 249 | + """Integral adds a conversion to int and the bit-string operations.""" | ||
| 250 | + | ||
| 251 | + @abstractmethod | ||
| 252 | + def __int__(self): | ||
| 253 | + raise NotImplementedError | ||
| 254 | + | ||
| 255 | + def __index__(self): | ||
| 256 | + return int(self) | ||
| 257 | + | ||
| 258 | + @abstractmethod | ||
| 259 | + def __pow__(self, exponent, modulus): | ||
| 260 | + """self ** exponent % modulus, but maybe faster. | ||
| 261 | + | ||
| 262 | + Implement this if you want to support the 3-argument version | ||
| 263 | + of pow(). Otherwise, just implement the 2-argument version | ||
| 264 | + described in Complex. Raise a TypeError if exponent < 0 or any | ||
| 265 | + argument isn't Integral. | ||
| 266 | + """ | ||
| 267 | + raise NotImplementedError | ||
| 268 | + | ||
| 269 | + @abstractmethod | ||
| 270 | + def __lshift__(self, other): | ||
| 271 | + raise NotImplementedError | ||
| 272 | + | ||
| 273 | + @abstractmethod | ||
| 274 | + def __rlshift__(self, other): | ||
| 275 | + raise NotImplementedError | ||
| 276 | + | ||
| 277 | + @abstractmethod | ||
| 278 | + def __rshift__(self, other): | ||
| 279 | + raise NotImplementedError | ||
| 280 | + | ||
| 281 | + @abstractmethod | ||
| 282 | + def __rrshift__(self, other): | ||
| 283 | + raise NotImplementedError | ||
| 284 | + | ||
| 285 | + @abstractmethod | ||
| 286 | + def __and__(self, other): | ||
| 287 | + raise NotImplementedError | ||
| 288 | + | ||
| 289 | + @abstractmethod | ||
| 290 | + def __rand__(self, other): | ||
| 291 | + raise NotImplementedError | ||
| 292 | + | ||
| 293 | + @abstractmethod | ||
| 294 | + def __xor__(self, other): | ||
| 295 | + raise NotImplementedError | ||
| 296 | + | ||
| 297 | + @abstractmethod | ||
| 298 | + def __rxor__(self, other): | ||
| 299 | + raise NotImplementedError | ||
| 300 | + | ||
| 301 | + @abstractmethod | ||
| 302 | + def __or__(self, other): | ||
| 303 | + raise NotImplementedError | ||
| 304 | + | ||
| 305 | + @abstractmethod | ||
| 306 | + def __ror__(self, other): | ||
| 307 | + raise NotImplementedError | ||
| 308 | + | ||
| 309 | + @abstractmethod | ||
| 310 | + def __invert__(self): | ||
| 311 | + raise NotImplementedError | ||
| 312 | + | ||
| 313 | + # Concrete implementations of Rational and Real abstract methods. | ||
| 314 | + def __float__(self): | ||
| 315 | + return float(int(self)) | ||
| 316 | + | ||
| 317 | + @property | ||
| 318 | + def numerator(self): | ||
| 319 | + return self | ||
| 320 | + | ||
| 321 | + @property | ||
| 322 | + def denominator(self): | ||
| 323 | + return 1 | ||
| 324 | + | ||
| 325 | + Integral.register(int) | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -0,0 +1,53 @@ | |||
| 1 | + """Unit tests for numbers.py.""" | ||
| 2 | + | ||
| 3 | + import unittest | ||
| 4 | + from test import test_support | ||
| 5 | + from numbers import Number | ||
| 6 | + from numbers import Exact, Inexact | ||
| 7 | + from numbers import Complex, Real, Rational, Integral | ||
| 8 | + import operator | ||
| 9 | + | ||
| 10 | + class TestNumbers(unittest.TestCase): | ||
| 11 | + def test_int(self): | ||
| 12 | + self.failUnless(issubclass(int, Integral)) | ||
| 13 | + self.failUnless(issubclass(int, Complex)) | ||
| 14 | + self.failUnless(issubclass(int, Exact)) | ||
| 15 | + self.failIf(issubclass(int, Inexact)) | ||
| 16 | + | ||
| 17 | + self.assertEqual(7, int(7).real) | ||
| 18 | + self.assertEqual(0, int(7).imag) | ||
| 19 | + self.assertEqual(7, int(7).conjugate()) | ||
| 20 | + self.assertEqual(7, int(7).numerator) | ||
| 21 | + self.assertEqual(1, int(7).denominator) | ||
| 22 | + | ||
| 23 | + def test_float(self): | ||
| 24 | + self.failIf(issubclass(float, Rational)) | ||
| 25 | + self.failUnless(issubclass(float, Real)) | ||
| 26 | + self.failIf(issubclass(float, Exact)) | ||
| 27 | + self.failUnless(issubclass(float, Inexact)) | ||
| 28 | + | ||
| 29 | + self.assertEqual(7.3, float(7.3).real) | ||
| 30 | + self.assertEqual(0, float(7.3).imag) | ||
| 31 | + self.assertEqual(7.3, float(7.3).conjugate()) | ||
| 32 | + | ||
| 33 | + def test_complex(self): | ||
| 34 | + self.failIf(issubclass(complex, Real)) | ||
| 35 | + self.failUnless(issubclass(complex, Complex)) | ||
| 36 | + self.failIf(issubclass(complex, Exact)) | ||
| 37 | + self.failUnless(issubclass(complex, Inexact)) | ||
| 38 | + | ||
| 39 | + c1, c2 = complex(3, 2), complex(4,1) | ||
| 40 | + # TODO: Uncomment this test when trunc() exists. | ||
| 41 | + #self.assertRaises(None, trunc, c1) | ||
| 42 | + self.assertRaises(TypeError, operator.mod, c1, c2) | ||
| 43 | + self.assertRaises(TypeError, divmod, c1, c2) | ||
| 44 | + self.assertRaises(TypeError, operator.floordiv, c1, c2) | ||
| 45 | + self.assertRaises(TypeError, float, c1) | ||
| 46 | + self.assertRaises(TypeError, int, c1) | ||
| 47 | + | ||
| 48 | + def test_main(): | ||
| 49 | + test_support.run_unittest(TestNumbers) | ||
| 50 | + | ||
| 51 | + | ||
| 52 | + if __name__ == "__main__": | ||
| 53 | + unittest.main() | ||
| Back | FazBrowse Home | New Git URL |
0 commit comments