From 3404b3ce2acfc550571f06e70526ff75a34effeb Mon Sep 17 00:00:00 2001 From: Jeffrey Yasskin Date: Fri, 7 Sep 2007 15:15:49 +0000 Subject: Check in some documentation tweaks for PEP 3141, add some tests, and implement the promotion to complex on pow(negative, fraction). --- Lib/numbers.py | 77 +++++++++++++++++++++++++++++++++++++----------- Lib/test/test_builtin.py | 4 ++- Lib/test/test_complex.py | 8 +---- Lib/test/test_descr.py | 5 ---- Objects/floatobject.c | 7 +++-- 5 files changed, 68 insertions(+), 33 deletions(-) diff --git a/Lib/numbers.py b/Lib/numbers.py index 8678a3a..34cc803 100644 --- a/Lib/numbers.py +++ b/Lib/numbers.py @@ -1,7 +1,9 @@ # Copyright 2007 Google, Inc. All Rights Reserved. # Licensed to PSF under a Contributor Agreement. -"""Abstract Base Classes (ABCs) for numbers, according to PEP 3141.""" +"""Abstract Base Classes (ABCs) for numbers, according to PEP 3141. + +TODO: Fill out more detailed documentation on the operators.""" from abc import ABCMeta, abstractmethod, abstractproperty @@ -56,10 +58,10 @@ class Complex(Number): @abstractmethod def __complex__(self): - """Return a builtin complex instance.""" + """Return a builtin complex instance. Called for complex(self).""" def __bool__(self): - """True if self != 0.""" + """True if self != 0. Called for bool(self).""" return self != 0 @abstractproperty @@ -80,53 +82,64 @@ class Complex(Number): @abstractmethod def __add__(self, other): + """self + other""" raise NotImplementedError @abstractmethod def __radd__(self, other): + """other + self""" raise NotImplementedError @abstractmethod def __neg__(self): + """-self""" raise NotImplementedError def __pos__(self): + """+self""" return self def __sub__(self, other): + """self - other""" return self + -other def __rsub__(self, other): + """other - self""" return -self + other @abstractmethod def __mul__(self, other): + """self * other""" raise NotImplementedError @abstractmethod def __rmul__(self, other): + """other * self""" raise NotImplementedError @abstractmethod def __div__(self, other): + """self / other""" raise NotImplementedError @abstractmethod def __rdiv__(self, other): + """other / self""" raise NotImplementedError @abstractmethod def __pow__(self, exponent): - """Like division, a**b should promote to complex when necessary.""" + """Like division, self**exponent should promote to complex when necessary.""" raise NotImplementedError @abstractmethod def __rpow__(self, base): + """base ** self""" raise NotImplementedError @abstractmethod def __abs__(self): - """Returns the Real distance from 0.""" + """Returns the Real distance from 0. Called for abs(self).""" raise NotImplementedError @abstractmethod @@ -136,9 +149,11 @@ class Complex(Number): @abstractmethod def __eq__(self, other): + """self == other""" raise NotImplementedError def __ne__(self, other): + """self != other""" return not (self == other) Complex.register(complex) @@ -155,12 +170,14 @@ class Real(Complex): @abstractmethod def __float__(self): - """Any Real can be converted to a native float object.""" + """Any Real can be converted to a native float object. + + Called for float(self).""" raise NotImplementedError @abstractmethod def __trunc__(self): - """Truncates self to an Integral. + """trunc(self): Truncates self to an Integral. Returns an Integral i such that: * i>0 iff self>0 @@ -169,7 +186,7 @@ class Real(Complex): raise NotImplementedError def __divmod__(self, other): - """The pair (self // other, self % other). + """divmod(self, other): The pair (self // other, self % other). Sometimes this can be computed faster than the pair of operations. @@ -177,7 +194,7 @@ class Real(Complex): return (self // other, self % other) def __rdivmod__(self, other): - """The pair (self // other, self % other). + """divmod(other, self): The pair (self // other, self % other). Sometimes this can be computed faster than the pair of operations. @@ -186,40 +203,49 @@ class Real(Complex): @abstractmethod def __floordiv__(self, other): - """The floor() of self/other.""" + """self // other: The floor() of self/other.""" raise NotImplementedError @abstractmethod def __rfloordiv__(self, other): - """The floor() of other/self.""" + """other // self: The floor() of other/self.""" raise NotImplementedError @abstractmethod def __mod__(self, other): + """self % other""" raise NotImplementedError @abstractmethod def __rmod__(self, other): + """other % self""" raise NotImplementedError @abstractmethod def __lt__(self, other): - """< on Reals defines a total ordering, except perhaps for NaN.""" + """self < other + + < on Reals defines a total ordering, except perhaps for NaN.""" raise NotImplementedError + @abstractmethod def __le__(self, other): + """self <= other""" raise NotImplementedError # Concrete implementations of Complex abstract methods. def __complex__(self): + """complex(self) == complex(float(self), 0)""" return complex(float(self)) @property def real(self): + """Real numbers are their real component.""" return self @property def imag(self): + """Real numbers have no imaginary component.""" return 0 def conjugate(self): @@ -242,6 +268,7 @@ class Rational(Real, Exact): # Concrete implementation of Real's conversion to float. def __float__(self): + """float(self) = self.numerator / self.denominator""" return self.numerator / self.denominator @@ -250,76 +277,92 @@ class Integral(Rational): @abstractmethod def __int__(self): + """int(self)""" raise NotImplementedError def __index__(self): + """index(self)""" return int(self) @abstractmethod - def __pow__(self, exponent, modulus): + def __pow__(self, exponent, modulus=None): """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. + Accept the modulus argument if you want to support the + 3-argument version of pow(). Raise a TypeError if exponent < 0 + or any argument isn't Integral. Otherwise, just implement the + 2-argument version described in Complex. """ raise NotImplementedError @abstractmethod def __lshift__(self, other): + """self << other""" raise NotImplementedError @abstractmethod def __rlshift__(self, other): + """other << self""" raise NotImplementedError @abstractmethod def __rshift__(self, other): + """self >> other""" raise NotImplementedError @abstractmethod def __rrshift__(self, other): + """other >> self""" raise NotImplementedError @abstractmethod def __and__(self, other): + """self & other""" raise NotImplementedError @abstractmethod def __rand__(self, other): + """other & self""" raise NotImplementedError @abstractmethod def __xor__(self, other): + """self ^ other""" raise NotImplementedError @abstractmethod def __rxor__(self, other): + """other ^ self""" raise NotImplementedError @abstractmethod def __or__(self, other): + """self | other""" raise NotImplementedError @abstractmethod def __ror__(self, other): + """other | self""" raise NotImplementedError @abstractmethod def __invert__(self): + """~self""" raise NotImplementedError # Concrete implementations of Rational and Real abstract methods. def __float__(self): + """float(self) == float(int(self))""" return float(int(self)) @property def numerator(self): + """Integers are their own numerators.""" return self @property def denominator(self): + """Integers have a denominator of 1.""" return 1 Integral.register(int) diff --git a/Lib/test/test_builtin.py b/Lib/test/test_builtin.py index d9633af..b6b45ee 100644 --- a/Lib/test/test_builtin.py +++ b/Lib/test/test_builtin.py @@ -1358,11 +1358,13 @@ class BuiltinTest(unittest.TestCase): else: self.assertAlmostEqual(pow(x, y, z), 24.0) + self.assertAlmostEqual(pow(-1, 0.5), 1j) + self.assertAlmostEqual(pow(-1, 1/3), 0.5 + 0.8660254037844386j) + self.assertRaises(TypeError, pow, -1, -2, 3) self.assertRaises(ValueError, pow, 1, 2, 0) self.assertRaises(TypeError, pow, -1, -2, 3) self.assertRaises(ValueError, pow, 1, 2, 0) - self.assertRaises(ValueError, pow, -342.43, 0.234) self.assertRaises(TypeError, pow) diff --git a/Lib/test/test_complex.py b/Lib/test/test_complex.py index b4082d9..5397ad6 100644 --- a/Lib/test/test_complex.py +++ b/Lib/test/test_complex.py @@ -1,13 +1,6 @@ import unittest, os from test import test_support -import warnings -warnings.filterwarnings( - "ignore", - category=DeprecationWarning, - message=".*complex divmod.*are deprecated" -) - from random import random # These tests ensure that complex math does the right thing @@ -108,6 +101,7 @@ class ComplexTest(unittest.TestCase): # % is no longer supported on complex numbers self.assertRaises(TypeError, (1+1j).__mod__, 0+0j) self.assertRaises(TypeError, lambda: (3.33+4.43j) % 0) + self.assertRaises(TypeError, (1+1j).__mod__, 4.3j) def test_divmod(self): self.assertRaises(TypeError, divmod, 1+1j, 1+0j) diff --git a/Lib/test/test_descr.py b/Lib/test/test_descr.py index 47b647c..bfa6a64 100644 --- a/Lib/test/test_descr.py +++ b/Lib/test/test_descr.py @@ -3,13 +3,8 @@ from test.test_support import verify, vereq, verbose, TestFailed, TESTFN from test.test_support import get_original_stdout from copy import deepcopy -import warnings import types -warnings.filterwarnings("ignore", - r'complex divmod\(\), // and % are deprecated$', - DeprecationWarning, r'(|%s)$' % __name__) - def veris(a, b): if a is not b: raise TestFailed("%r is %r" % (a, b)) diff --git a/Objects/floatobject.c b/Objects/floatobject.c index 7c489d9..eb540e6 100644 --- a/Objects/floatobject.c +++ b/Objects/floatobject.c @@ -680,9 +680,10 @@ float_pow(PyObject *v, PyObject *w, PyObject *z) * bugs so we have to figure it out ourselves. */ if (iw != floor(iw)) { - PyErr_SetString(PyExc_ValueError, "negative number " - "cannot be raised to a fractional power"); - return NULL; + /* Negative numbers raised to fractional powers + * become complex. + */ + return PyComplex_Type.tp_as_number->nb_power(v, w, z); } /* iw is an exact integer, albeit perhaps a very large one. * -1 raised to an exact integer should never be exceptional. -- cgit v0.12