Check in some documentation tweaks for PEP 3141, add some tests, and implement

the promotion to complex on pow(negative, fraction).
This commit is contained in:
Jeffrey Yasskin 2007-09-07 15:15:49 +00:00
parent aaaef110dc
commit 3404b3ce2a
5 changed files with 68 additions and 33 deletions

View file

@ -1,7 +1,9 @@
# Copyright 2007 Google, Inc. All Rights Reserved. # Copyright 2007 Google, Inc. All Rights Reserved.
# Licensed to PSF under a Contributor Agreement. # 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 from abc import ABCMeta, abstractmethod, abstractproperty
@ -56,10 +58,10 @@ class Complex(Number):
@abstractmethod @abstractmethod
def __complex__(self): def __complex__(self):
"""Return a builtin complex instance.""" """Return a builtin complex instance. Called for complex(self)."""
def __bool__(self): def __bool__(self):
"""True if self != 0.""" """True if self != 0. Called for bool(self)."""
return self != 0 return self != 0
@abstractproperty @abstractproperty
@ -80,53 +82,64 @@ class Complex(Number):
@abstractmethod @abstractmethod
def __add__(self, other): def __add__(self, other):
"""self + other"""
raise NotImplementedError raise NotImplementedError
@abstractmethod @abstractmethod
def __radd__(self, other): def __radd__(self, other):
"""other + self"""
raise NotImplementedError raise NotImplementedError
@abstractmethod @abstractmethod
def __neg__(self): def __neg__(self):
"""-self"""
raise NotImplementedError raise NotImplementedError
def __pos__(self): def __pos__(self):
"""+self"""
return self return self
def __sub__(self, other): def __sub__(self, other):
"""self - other"""
return self + -other return self + -other
def __rsub__(self, other): def __rsub__(self, other):
"""other - self"""
return -self + other return -self + other
@abstractmethod @abstractmethod
def __mul__(self, other): def __mul__(self, other):
"""self * other"""
raise NotImplementedError raise NotImplementedError
@abstractmethod @abstractmethod
def __rmul__(self, other): def __rmul__(self, other):
"""other * self"""
raise NotImplementedError raise NotImplementedError
@abstractmethod @abstractmethod
def __div__(self, other): def __div__(self, other):
"""self / other"""
raise NotImplementedError raise NotImplementedError
@abstractmethod @abstractmethod
def __rdiv__(self, other): def __rdiv__(self, other):
"""other / self"""
raise NotImplementedError raise NotImplementedError
@abstractmethod @abstractmethod
def __pow__(self, exponent): 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 raise NotImplementedError
@abstractmethod @abstractmethod
def __rpow__(self, base): def __rpow__(self, base):
"""base ** self"""
raise NotImplementedError raise NotImplementedError
@abstractmethod @abstractmethod
def __abs__(self): def __abs__(self):
"""Returns the Real distance from 0.""" """Returns the Real distance from 0. Called for abs(self)."""
raise NotImplementedError raise NotImplementedError
@abstractmethod @abstractmethod
@ -136,9 +149,11 @@ class Complex(Number):
@abstractmethod @abstractmethod
def __eq__(self, other): def __eq__(self, other):
"""self == other"""
raise NotImplementedError raise NotImplementedError
def __ne__(self, other): def __ne__(self, other):
"""self != other"""
return not (self == other) return not (self == other)
Complex.register(complex) Complex.register(complex)
@ -155,12 +170,14 @@ class Real(Complex):
@abstractmethod @abstractmethod
def __float__(self): 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 raise NotImplementedError
@abstractmethod @abstractmethod
def __trunc__(self): def __trunc__(self):
"""Truncates self to an Integral. """trunc(self): Truncates self to an Integral.
Returns an Integral i such that: Returns an Integral i such that:
* i>0 iff self>0 * i>0 iff self>0
@ -169,7 +186,7 @@ class Real(Complex):
raise NotImplementedError raise NotImplementedError
def __divmod__(self, other): 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 Sometimes this can be computed faster than the pair of
operations. operations.
@ -177,7 +194,7 @@ class Real(Complex):
return (self // other, self % other) return (self // other, self % other)
def __rdivmod__(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 Sometimes this can be computed faster than the pair of
operations. operations.
@ -186,40 +203,49 @@ class Real(Complex):
@abstractmethod @abstractmethod
def __floordiv__(self, other): def __floordiv__(self, other):
"""The floor() of self/other.""" """self // other: The floor() of self/other."""
raise NotImplementedError raise NotImplementedError
@abstractmethod @abstractmethod
def __rfloordiv__(self, other): def __rfloordiv__(self, other):
"""The floor() of other/self.""" """other // self: The floor() of other/self."""
raise NotImplementedError raise NotImplementedError
@abstractmethod @abstractmethod
def __mod__(self, other): def __mod__(self, other):
"""self % other"""
raise NotImplementedError raise NotImplementedError
@abstractmethod @abstractmethod
def __rmod__(self, other): def __rmod__(self, other):
"""other % self"""
raise NotImplementedError raise NotImplementedError
@abstractmethod @abstractmethod
def __lt__(self, other): 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 raise NotImplementedError
@abstractmethod
def __le__(self, other): def __le__(self, other):
"""self <= other"""
raise NotImplementedError raise NotImplementedError
# Concrete implementations of Complex abstract methods. # Concrete implementations of Complex abstract methods.
def __complex__(self): def __complex__(self):
"""complex(self) == complex(float(self), 0)"""
return complex(float(self)) return complex(float(self))
@property @property
def real(self): def real(self):
"""Real numbers are their real component."""
return self return self
@property @property
def imag(self): def imag(self):
"""Real numbers have no imaginary component."""
return 0 return 0
def conjugate(self): def conjugate(self):
@ -242,6 +268,7 @@ class Rational(Real, Exact):
# Concrete implementation of Real's conversion to float. # Concrete implementation of Real's conversion to float.
def __float__(self): def __float__(self):
"""float(self) = self.numerator / self.denominator"""
return self.numerator / self.denominator return self.numerator / self.denominator
@ -250,76 +277,92 @@ class Integral(Rational):
@abstractmethod @abstractmethod
def __int__(self): def __int__(self):
"""int(self)"""
raise NotImplementedError raise NotImplementedError
def __index__(self): def __index__(self):
"""index(self)"""
return int(self) return int(self)
@abstractmethod @abstractmethod
def __pow__(self, exponent, modulus): def __pow__(self, exponent, modulus=None):
"""self ** exponent % modulus, but maybe faster. """self ** exponent % modulus, but maybe faster.
Implement this if you want to support the 3-argument version Accept the modulus argument if you want to support the
of pow(). Otherwise, just implement the 2-argument version 3-argument version of pow(). Raise a TypeError if exponent < 0
described in Complex. Raise a TypeError if exponent < 0 or any or any argument isn't Integral. Otherwise, just implement the
argument isn't Integral. 2-argument version described in Complex.
""" """
raise NotImplementedError raise NotImplementedError
@abstractmethod @abstractmethod
def __lshift__(self, other): def __lshift__(self, other):
"""self << other"""
raise NotImplementedError raise NotImplementedError
@abstractmethod @abstractmethod
def __rlshift__(self, other): def __rlshift__(self, other):
"""other << self"""
raise NotImplementedError raise NotImplementedError
@abstractmethod @abstractmethod
def __rshift__(self, other): def __rshift__(self, other):
"""self >> other"""
raise NotImplementedError raise NotImplementedError
@abstractmethod @abstractmethod
def __rrshift__(self, other): def __rrshift__(self, other):
"""other >> self"""
raise NotImplementedError raise NotImplementedError
@abstractmethod @abstractmethod
def __and__(self, other): def __and__(self, other):
"""self & other"""
raise NotImplementedError raise NotImplementedError
@abstractmethod @abstractmethod
def __rand__(self, other): def __rand__(self, other):
"""other & self"""
raise NotImplementedError raise NotImplementedError
@abstractmethod @abstractmethod
def __xor__(self, other): def __xor__(self, other):
"""self ^ other"""
raise NotImplementedError raise NotImplementedError
@abstractmethod @abstractmethod
def __rxor__(self, other): def __rxor__(self, other):
"""other ^ self"""
raise NotImplementedError raise NotImplementedError
@abstractmethod @abstractmethod
def __or__(self, other): def __or__(self, other):
"""self | other"""
raise NotImplementedError raise NotImplementedError
@abstractmethod @abstractmethod
def __ror__(self, other): def __ror__(self, other):
"""other | self"""
raise NotImplementedError raise NotImplementedError
@abstractmethod @abstractmethod
def __invert__(self): def __invert__(self):
"""~self"""
raise NotImplementedError raise NotImplementedError
# Concrete implementations of Rational and Real abstract methods. # Concrete implementations of Rational and Real abstract methods.
def __float__(self): def __float__(self):
"""float(self) == float(int(self))"""
return float(int(self)) return float(int(self))
@property @property
def numerator(self): def numerator(self):
"""Integers are their own numerators."""
return self return self
@property @property
def denominator(self): def denominator(self):
"""Integers have a denominator of 1."""
return 1 return 1
Integral.register(int) Integral.register(int)

View file

@ -1358,11 +1358,13 @@ class BuiltinTest(unittest.TestCase):
else: else:
self.assertAlmostEqual(pow(x, y, z), 24.0) 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(TypeError, pow, -1, -2, 3)
self.assertRaises(ValueError, pow, 1, 2, 0) self.assertRaises(ValueError, pow, 1, 2, 0)
self.assertRaises(TypeError, pow, -1, -2, 3) self.assertRaises(TypeError, pow, -1, -2, 3)
self.assertRaises(ValueError, pow, 1, 2, 0) self.assertRaises(ValueError, pow, 1, 2, 0)
self.assertRaises(ValueError, pow, -342.43, 0.234)
self.assertRaises(TypeError, pow) self.assertRaises(TypeError, pow)

View file

@ -1,13 +1,6 @@
import unittest, os import unittest, os
from test import test_support from test import test_support
import warnings
warnings.filterwarnings(
"ignore",
category=DeprecationWarning,
message=".*complex divmod.*are deprecated"
)
from random import random from random import random
# These tests ensure that complex math does the right thing # 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 # % is no longer supported on complex numbers
self.assertRaises(TypeError, (1+1j).__mod__, 0+0j) self.assertRaises(TypeError, (1+1j).__mod__, 0+0j)
self.assertRaises(TypeError, lambda: (3.33+4.43j) % 0) self.assertRaises(TypeError, lambda: (3.33+4.43j) % 0)
self.assertRaises(TypeError, (1+1j).__mod__, 4.3j)
def test_divmod(self): def test_divmod(self):
self.assertRaises(TypeError, divmod, 1+1j, 1+0j) self.assertRaises(TypeError, divmod, 1+1j, 1+0j)

View file

@ -3,13 +3,8 @@
from test.test_support import verify, vereq, verbose, TestFailed, TESTFN from test.test_support import verify, vereq, verbose, TestFailed, TESTFN
from test.test_support import get_original_stdout from test.test_support import get_original_stdout
from copy import deepcopy from copy import deepcopy
import warnings
import types import types
warnings.filterwarnings("ignore",
r'complex divmod\(\), // and % are deprecated$',
DeprecationWarning, r'(<string>|%s)$' % __name__)
def veris(a, b): def veris(a, b):
if a is not b: if a is not b:
raise TestFailed("%r is %r" % (a, b)) raise TestFailed("%r is %r" % (a, b))

View file

@ -680,9 +680,10 @@ float_pow(PyObject *v, PyObject *w, PyObject *z)
* bugs so we have to figure it out ourselves. * bugs so we have to figure it out ourselves.
*/ */
if (iw != floor(iw)) { if (iw != floor(iw)) {
PyErr_SetString(PyExc_ValueError, "negative number " /* Negative numbers raised to fractional powers
"cannot be raised to a fractional power"); * become complex.
return NULL; */
return PyComplex_Type.tp_as_number->nb_power(v, w, z);
} }
/* iw is an exact integer, albeit perhaps a very large one. /* iw is an exact integer, albeit perhaps a very large one.
* -1 raised to an exact integer should never be exceptional. * -1 raised to an exact integer should never be exceptional.