Commit 3404b3ce authored by Jeffrey Yasskin's avatar Jeffrey Yasskin

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

the promotion to complex on pow(negative, fraction).
parent aaaef110
# 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): ...@@ -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): ...@@ -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): ...@@ -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): ...@@ -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): ...@@ -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): ...@@ -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): ...@@ -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): ...@@ -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): ...@@ -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)
...@@ -1358,11 +1358,13 @@ class BuiltinTest(unittest.TestCase): ...@@ -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)
......
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): ...@@ -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)
......
...@@ -3,13 +3,8 @@ ...@@ -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))
......
...@@ -680,9 +680,10 @@ float_pow(PyObject *v, PyObject *w, PyObject *z) ...@@ -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.
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment