2007-04-25 16:42:59 -04:00
|
|
|
|
PEP: 3141
|
2007-05-16 20:23:41 -04:00
|
|
|
|
Title: A Type Hierarchy for Numbers
|
2007-04-25 16:42:59 -04:00
|
|
|
|
Version: $Revision$
|
|
|
|
|
Last-Modified: $Date$
|
|
|
|
|
Author: Jeffrey Yasskin <jyasskin@gmail.com>
|
2007-12-18 23:16:33 -05:00
|
|
|
|
Status: Accepted
|
2007-04-25 16:42:59 -04:00
|
|
|
|
Type: Standards Track
|
|
|
|
|
Content-Type: text/x-rst
|
|
|
|
|
Created: 23-Apr-2007
|
2007-08-02 15:43:38 -04:00
|
|
|
|
Post-History: 25-Apr-2007, 16-May-2007, 02-Aug-2007
|
2007-04-25 16:42:59 -04:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Abstract
|
|
|
|
|
========
|
|
|
|
|
|
2007-05-16 20:23:41 -04:00
|
|
|
|
This proposal defines a hierarchy of Abstract Base Classes (ABCs) (PEP
|
|
|
|
|
3119) to represent number-like classes. It proposes a hierarchy of
|
2007-07-31 15:19:57 -04:00
|
|
|
|
``Number :> Complex :> Real :> Rational :> Integral`` where ``A :> B``
|
2008-06-05 19:12:33 -04:00
|
|
|
|
means "A is a supertype of B". The hierarchy is inspired by Scheme's
|
|
|
|
|
numeric tower [#schemetower]_.
|
2007-04-25 16:42:59 -04:00
|
|
|
|
|
|
|
|
|
Rationale
|
|
|
|
|
=========
|
|
|
|
|
|
|
|
|
|
Functions that take numbers as arguments should be able to determine
|
|
|
|
|
the properties of those numbers, and if and when overloading based on
|
|
|
|
|
types is added to the language, should be overloadable based on the
|
2007-05-16 20:23:41 -04:00
|
|
|
|
types of the arguments. For example, slicing requires its arguments to
|
2007-07-31 15:19:57 -04:00
|
|
|
|
be ``Integrals``, and the functions in the ``math`` module require
|
2007-05-16 20:23:41 -04:00
|
|
|
|
their arguments to be ``Real``.
|
2007-04-25 16:42:59 -04:00
|
|
|
|
|
|
|
|
|
Specification
|
|
|
|
|
=============
|
|
|
|
|
|
2007-07-31 15:19:57 -04:00
|
|
|
|
This PEP specifies a set of Abstract Base Classes, and suggests a
|
|
|
|
|
general strategy for implementing some of the methods. It uses
|
|
|
|
|
terminology from PEP 3119, but the hierarchy is intended to be
|
|
|
|
|
meaningful for any systematic method of defining sets of classes.
|
2007-05-16 20:23:41 -04:00
|
|
|
|
|
2007-08-02 15:43:38 -04:00
|
|
|
|
The type checks in the standard library should use these classes
|
|
|
|
|
instead of the concrete built-ins.
|
|
|
|
|
|
2007-05-16 20:23:41 -04:00
|
|
|
|
|
|
|
|
|
Numeric Classes
|
|
|
|
|
---------------
|
|
|
|
|
|
|
|
|
|
We begin with a Number class to make it easy for people to be fuzzy
|
|
|
|
|
about what kind of number they expect. This class only helps with
|
2007-08-01 13:11:55 -04:00
|
|
|
|
overloading; it doesn't provide any operations. ::
|
|
|
|
|
|
|
|
|
|
class Number(metaclass=ABCMeta): pass
|
|
|
|
|
|
2007-07-31 15:19:57 -04:00
|
|
|
|
|
|
|
|
|
Most implementations of complex numbers will be hashable, but if you
|
|
|
|
|
need to rely on that, you'll have to check it explicitly: mutable
|
2007-08-22 18:07:53 -04:00
|
|
|
|
numbers are supported by this hierarchy. ::
|
2007-07-31 15:19:57 -04:00
|
|
|
|
|
|
|
|
|
class Complex(Number):
|
|
|
|
|
"""Complex defines the operations that work on the builtin complex type.
|
|
|
|
|
|
2007-12-06 12:49:44 -05:00
|
|
|
|
In short, those are: conversion to complex, bool(), .real, .imag,
|
|
|
|
|
+, -, *, /, **, abs(), .conjugate(), ==, and !=.
|
2007-05-16 20:23:41 -04:00
|
|
|
|
|
2007-08-01 13:11:55 -04:00
|
|
|
|
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.
|
2007-05-16 20:23:41 -04:00
|
|
|
|
"""
|
2007-07-31 17:14:18 -04:00
|
|
|
|
|
2007-05-16 20:23:41 -04:00
|
|
|
|
@abstractmethod
|
|
|
|
|
def __complex__(self):
|
2007-07-31 15:19:57 -04:00
|
|
|
|
"""Return a builtin complex instance."""
|
|
|
|
|
|
2007-08-01 13:11:55 -04:00
|
|
|
|
def __bool__(self):
|
|
|
|
|
"""True if self != 0."""
|
|
|
|
|
return self != 0
|
|
|
|
|
|
|
|
|
|
@abstractproperty
|
2007-05-16 20:23:41 -04:00
|
|
|
|
def real(self):
|
2007-08-01 13:11:55 -04:00
|
|
|
|
"""Retrieve the real component of this number.
|
|
|
|
|
|
|
|
|
|
This should subclass Real.
|
|
|
|
|
"""
|
2007-07-31 15:19:57 -04:00
|
|
|
|
raise NotImplementedError
|
2007-07-31 17:14:18 -04:00
|
|
|
|
|
2007-08-01 13:11:55 -04:00
|
|
|
|
@abstractproperty
|
2007-05-16 20:23:41 -04:00
|
|
|
|
def imag(self):
|
2007-08-01 13:11:55 -04:00
|
|
|
|
"""Retrieve the real component of this number.
|
|
|
|
|
|
|
|
|
|
This should subclass Real.
|
|
|
|
|
"""
|
2007-07-31 15:19:57 -04:00
|
|
|
|
raise NotImplementedError
|
2007-05-16 20:23:41 -04:00
|
|
|
|
|
2007-07-31 15:19:57 -04:00
|
|
|
|
@abstractmethod
|
|
|
|
|
def __add__(self, other):
|
|
|
|
|
raise NotImplementedError
|
2007-07-31 17:14:18 -04:00
|
|
|
|
|
2007-07-31 15:19:57 -04:00
|
|
|
|
@abstractmethod
|
2007-08-01 13:11:55 -04:00
|
|
|
|
def __radd__(self, other):
|
2007-07-31 15:19:57 -04:00
|
|
|
|
raise NotImplementedError
|
2007-07-31 17:14:18 -04:00
|
|
|
|
|
2007-07-31 15:19:57 -04:00
|
|
|
|
@abstractmethod
|
2007-05-16 20:23:41 -04:00
|
|
|
|
def __neg__(self):
|
2007-07-31 15:19:57 -04:00
|
|
|
|
raise NotImplementedError
|
2007-07-31 17:14:18 -04:00
|
|
|
|
|
2007-08-01 13:11:55 -04:00
|
|
|
|
def __pos__(self):
|
2007-12-06 12:49:44 -05:00
|
|
|
|
"""Coerces self to whatever class defines the method."""
|
2007-08-22 18:07:53 -04:00
|
|
|
|
raise NotImplementedError
|
2007-08-01 13:11:55 -04:00
|
|
|
|
|
|
|
|
|
def __sub__(self, other):
|
|
|
|
|
return self + -other
|
|
|
|
|
|
|
|
|
|
def __rsub__(self, other):
|
|
|
|
|
return -self + other
|
|
|
|
|
|
2007-07-31 15:19:57 -04:00
|
|
|
|
@abstractmethod
|
|
|
|
|
def __mul__(self, other):
|
|
|
|
|
raise NotImplementedError
|
2007-07-31 17:14:18 -04:00
|
|
|
|
|
2007-08-01 13:11:55 -04:00
|
|
|
|
@abstractmethod
|
|
|
|
|
def __rmul__(self, other):
|
|
|
|
|
raise NotImplementedError
|
|
|
|
|
|
2007-07-31 15:19:57 -04:00
|
|
|
|
@abstractmethod
|
|
|
|
|
def __div__(self, other):
|
2007-12-06 12:49:44 -05:00
|
|
|
|
"""a/b; should promote to float or complex when necessary."""
|
2007-07-31 15:19:57 -04:00
|
|
|
|
raise NotImplementedError
|
2007-05-16 20:23:41 -04:00
|
|
|
|
|
2007-08-01 13:11:55 -04:00
|
|
|
|
@abstractmethod
|
|
|
|
|
def __rdiv__(self, other):
|
|
|
|
|
raise NotImplementedError
|
|
|
|
|
|
|
|
|
|
@abstractmethod
|
|
|
|
|
def __pow__(self, exponent):
|
2007-12-06 12:49:44 -05:00
|
|
|
|
"""a**b; should promote to float or complex when necessary."""
|
2007-08-01 13:11:55 -04:00
|
|
|
|
raise NotImplementedError
|
|
|
|
|
|
|
|
|
|
@abstractmethod
|
|
|
|
|
def __rpow__(self, base):
|
|
|
|
|
raise NotImplementedError
|
|
|
|
|
|
2007-07-31 15:19:57 -04:00
|
|
|
|
@abstractmethod
|
2007-05-16 20:23:41 -04:00
|
|
|
|
def __abs__(self):
|
2007-07-31 15:19:57 -04:00
|
|
|
|
"""Returns the Real distance from 0."""
|
|
|
|
|
raise NotImplementedError
|
2007-05-16 20:23:41 -04:00
|
|
|
|
|
2007-07-31 15:19:57 -04:00
|
|
|
|
@abstractmethod
|
2007-05-16 20:23:41 -04:00
|
|
|
|
def conjugate(self):
|
2007-07-31 15:19:57 -04:00
|
|
|
|
"""(x+y*i).conjugate() returns (x-y*i)."""
|
|
|
|
|
raise NotImplementedError
|
2007-05-16 20:23:41 -04:00
|
|
|
|
|
2007-07-31 15:19:57 -04:00
|
|
|
|
@abstractmethod
|
|
|
|
|
def __eq__(self, other):
|
|
|
|
|
raise NotImplementedError
|
2007-07-31 17:14:18 -04:00
|
|
|
|
|
2007-12-06 12:49:44 -05:00
|
|
|
|
# __ne__ is inherited from object and negates whatever __eq__ does.
|
2007-04-25 16:42:59 -04:00
|
|
|
|
|
|
|
|
|
|
2007-05-16 20:23:41 -04:00
|
|
|
|
The ``Real`` ABC indicates that the value is on the real line, and
|
|
|
|
|
supports the operations of the ``float`` builtin. Real numbers are
|
2007-08-01 13:11:55 -04:00
|
|
|
|
totally ordered except for NaNs (which this PEP basically ignores). ::
|
2007-07-31 15:19:57 -04:00
|
|
|
|
|
|
|
|
|
class Real(Complex):
|
|
|
|
|
"""To Complex, Real adds the operations that work on real numbers.
|
|
|
|
|
|
2007-12-06 12:49:44 -05:00
|
|
|
|
In short, those are: conversion to float, trunc(), math.floor(),
|
|
|
|
|
math.ceil(), round(), divmod(), //, %, <, <=, >, and >=.
|
2007-04-25 16:42:59 -04:00
|
|
|
|
|
2007-12-06 12:49:44 -05:00
|
|
|
|
Real also provides defaults for some of the derived operations.
|
2007-07-31 15:19:57 -04:00
|
|
|
|
"""
|
2007-07-31 17:14:18 -04:00
|
|
|
|
|
2007-12-06 12:49:44 -05:00
|
|
|
|
# XXX What to do about the __int__ implementation that's
|
2008-02-09 20:14:46 -05:00
|
|
|
|
# currently present on float? Get rid of it?
|
2007-12-06 12:49:44 -05:00
|
|
|
|
|
2007-05-16 20:23:41 -04:00
|
|
|
|
@abstractmethod
|
2007-08-01 13:11:55 -04:00
|
|
|
|
def __float__(self):
|
|
|
|
|
"""Any Real can be converted to a native float object."""
|
2007-07-31 15:19:57 -04:00
|
|
|
|
raise NotImplementedError
|
2007-08-01 13:11:55 -04:00
|
|
|
|
|
2007-07-31 15:19:57 -04:00
|
|
|
|
@abstractmethod
|
2007-08-01 13:11:55 -04:00
|
|
|
|
def __trunc__(self):
|
|
|
|
|
"""Truncates self to an Integral.
|
|
|
|
|
|
|
|
|
|
Returns an Integral i such that:
|
2007-12-06 12:49:44 -05:00
|
|
|
|
* i>=0 iff self>0;
|
|
|
|
|
* abs(i) <= abs(self);
|
2007-08-22 17:38:54 -04:00
|
|
|
|
* for any Integral j satisfying the first two conditions,
|
2007-12-06 12:49:44 -05:00
|
|
|
|
abs(i) >= abs(j) [i.e. i has "maximal" abs among those].
|
2007-08-22 17:38:54 -04:00
|
|
|
|
i.e. "truncate towards 0".
|
2007-08-01 13:11:55 -04:00
|
|
|
|
"""
|
2007-07-31 15:19:57 -04:00
|
|
|
|
raise NotImplementedError
|
2007-05-16 20:23:41 -04:00
|
|
|
|
|
2007-08-22 18:07:53 -04:00
|
|
|
|
@abstractmethod
|
|
|
|
|
def __floor__(self):
|
|
|
|
|
"""Finds the greatest Integral <= self."""
|
|
|
|
|
raise NotImplementedError
|
|
|
|
|
|
|
|
|
|
@abstractmethod
|
|
|
|
|
def __ceil__(self):
|
|
|
|
|
"""Finds the least Integral >= self."""
|
|
|
|
|
raise NotImplementedError
|
|
|
|
|
|
|
|
|
|
@abstractmethod
|
|
|
|
|
def __round__(self, ndigits:Integral=None):
|
|
|
|
|
"""Rounds self to ndigits decimal places, defaulting to 0.
|
|
|
|
|
|
2008-01-06 19:25:37 -05:00
|
|
|
|
If ndigits is omitted or None, returns an Integral,
|
|
|
|
|
otherwise returns a Real, preferably of the same type as
|
|
|
|
|
self. Types may choose which direction to round half. For
|
2008-02-09 20:14:46 -05:00
|
|
|
|
example, float rounds half toward even.
|
2008-01-06 19:25:37 -05:00
|
|
|
|
|
2007-08-22 18:07:53 -04:00
|
|
|
|
"""
|
|
|
|
|
raise NotImplementedError
|
|
|
|
|
|
2007-07-31 15:19:57 -04:00
|
|
|
|
def __divmod__(self, other):
|
2007-08-01 13:11:55 -04:00
|
|
|
|
"""The pair (self // other, self % other).
|
|
|
|
|
|
|
|
|
|
Sometimes this can be computed faster than the pair of
|
|
|
|
|
operations.
|
|
|
|
|
"""
|
2007-07-31 15:19:57 -04:00
|
|
|
|
return (self // other, self % other)
|
2007-07-31 17:14:18 -04:00
|
|
|
|
|
2007-08-01 13:11:55 -04:00
|
|
|
|
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)
|
|
|
|
|
|
2007-07-31 15:19:57 -04:00
|
|
|
|
@abstractmethod
|
|
|
|
|
def __floordiv__(self, other):
|
2007-08-02 15:43:38 -04:00
|
|
|
|
"""The floor() of self/other. Integral."""
|
2007-07-31 15:19:57 -04:00
|
|
|
|
raise NotImplementedError
|
2007-07-31 17:14:18 -04:00
|
|
|
|
|
2007-08-01 13:11:55 -04:00
|
|
|
|
@abstractmethod
|
|
|
|
|
def __rfloordiv__(self, other):
|
|
|
|
|
"""The floor() of other/self."""
|
|
|
|
|
raise NotImplementedError
|
|
|
|
|
|
2007-07-31 15:19:57 -04:00
|
|
|
|
@abstractmethod
|
|
|
|
|
def __mod__(self, other):
|
2007-08-22 18:07:53 -04:00
|
|
|
|
"""self % other
|
|
|
|
|
|
|
|
|
|
See
|
|
|
|
|
http://mail.python.org/pipermail/python-3000/2006-May/001735.html
|
|
|
|
|
and consider using "self/other - trunc(self/other)"
|
|
|
|
|
instead if you're worried about round-off errors.
|
|
|
|
|
"""
|
2007-08-01 13:11:55 -04:00
|
|
|
|
raise NotImplementedError
|
|
|
|
|
|
|
|
|
|
@abstractmethod
|
|
|
|
|
def __rmod__(self, other):
|
2007-08-22 18:07:53 -04:00
|
|
|
|
"""other % self"""
|
2007-07-31 15:19:57 -04:00
|
|
|
|
raise NotImplementedError
|
2007-05-16 20:23:41 -04:00
|
|
|
|
|
2007-07-31 15:19:57 -04:00
|
|
|
|
@abstractmethod
|
2007-05-16 20:23:41 -04:00
|
|
|
|
def __lt__(self, other):
|
2007-08-01 13:11:55 -04:00
|
|
|
|
"""< on Reals defines a total ordering, except perhaps for NaN."""
|
2007-07-31 15:19:57 -04:00
|
|
|
|
raise NotImplementedError
|
2007-07-31 17:14:18 -04:00
|
|
|
|
|
2007-08-02 15:43:38 -04:00
|
|
|
|
@abstractmethod
|
2007-07-31 15:19:57 -04:00
|
|
|
|
def __le__(self, other):
|
2007-08-01 13:11:55 -04:00
|
|
|
|
raise NotImplementedError
|
2007-07-31 15:19:57 -04:00
|
|
|
|
|
2007-12-06 12:49:44 -05:00
|
|
|
|
# __gt__ and __ge__ are automatically done by reversing the arguments.
|
|
|
|
|
# (But __le__ is not computed as the opposite of __gt__!)
|
|
|
|
|
|
2007-07-31 15:19:57 -04:00
|
|
|
|
# Concrete implementations of Complex abstract methods.
|
2007-12-06 12:49:44 -05:00
|
|
|
|
# Subclasses may override these, but don't have to.
|
2007-07-31 17:14:18 -04:00
|
|
|
|
|
2007-07-31 15:19:57 -04:00
|
|
|
|
def __complex__(self):
|
|
|
|
|
return complex(float(self))
|
2007-07-31 17:14:18 -04:00
|
|
|
|
|
2007-07-31 15:19:57 -04:00
|
|
|
|
@property
|
|
|
|
|
def real(self):
|
2007-12-06 12:49:44 -05:00
|
|
|
|
return +self
|
2007-07-31 17:14:18 -04:00
|
|
|
|
|
2007-07-31 15:19:57 -04:00
|
|
|
|
@property
|
|
|
|
|
def imag(self):
|
|
|
|
|
return 0
|
2007-05-16 20:23:41 -04:00
|
|
|
|
|
2007-08-01 13:11:55 -04:00
|
|
|
|
def conjugate(self):
|
|
|
|
|
"""Conjugate is a no-op for Reals."""
|
2007-12-06 12:49:44 -05:00
|
|
|
|
return +self
|
2007-08-01 13:11:55 -04:00
|
|
|
|
|
2007-05-16 20:23:41 -04:00
|
|
|
|
|
2007-12-06 12:49:44 -05:00
|
|
|
|
We should clean up Demo/classes/Rat.py and promote it into
|
2007-08-22 18:07:53 -04:00
|
|
|
|
rational.py in the standard library. Then it will implement the
|
|
|
|
|
Rational ABC. ::
|
2007-05-16 20:23:41 -04:00
|
|
|
|
|
|
|
|
|
class Rational(Real, Exact):
|
2007-07-31 15:19:57 -04:00
|
|
|
|
""".numerator and .denominator should be in lowest terms."""
|
2007-07-31 17:14:18 -04:00
|
|
|
|
|
2007-08-01 13:11:55 -04:00
|
|
|
|
@abstractproperty
|
2007-05-16 20:23:41 -04:00
|
|
|
|
def numerator(self):
|
|
|
|
|
raise NotImplementedError
|
2007-07-31 17:14:18 -04:00
|
|
|
|
|
2007-08-01 13:11:55 -04:00
|
|
|
|
@abstractproperty
|
2007-05-16 20:23:41 -04:00
|
|
|
|
def denominator(self):
|
|
|
|
|
raise NotImplementedError
|
|
|
|
|
|
2007-07-31 15:19:57 -04:00
|
|
|
|
# Concrete implementation of Real's conversion to float.
|
2007-12-06 12:49:44 -05:00
|
|
|
|
# (This invokes Integer.__div__().)
|
2007-08-01 13:11:55 -04:00
|
|
|
|
|
2007-05-16 20:23:41 -04:00
|
|
|
|
def __float__(self):
|
|
|
|
|
return self.numerator / self.denominator
|
|
|
|
|
|
|
|
|
|
|
2007-07-31 15:19:57 -04:00
|
|
|
|
And finally integers::
|
|
|
|
|
|
|
|
|
|
class Integral(Rational):
|
|
|
|
|
"""Integral adds a conversion to int and the bit-string operations."""
|
2007-07-31 17:14:18 -04:00
|
|
|
|
|
2007-07-31 15:19:57 -04:00
|
|
|
|
@abstractmethod
|
|
|
|
|
def __int__(self):
|
|
|
|
|
raise NotImplementedError
|
|
|
|
|
|
2007-08-01 13:11:55 -04:00
|
|
|
|
def __index__(self):
|
2008-02-09 20:14:46 -05:00
|
|
|
|
"""__index__() exists because float has __int__()."""
|
2007-08-01 13:11:55 -04:00
|
|
|
|
return int(self)
|
|
|
|
|
|
2007-07-31 15:19:57 -04:00
|
|
|
|
def __lshift__(self, other):
|
2008-06-10 20:26:20 -04:00
|
|
|
|
return int(self) << int(other)
|
2007-07-31 17:14:18 -04:00
|
|
|
|
|
2007-08-01 13:11:55 -04:00
|
|
|
|
def __rlshift__(self, other):
|
2008-06-10 20:26:20 -04:00
|
|
|
|
return int(other) << int(self)
|
2007-08-01 13:11:55 -04:00
|
|
|
|
|
2007-07-31 15:19:57 -04:00
|
|
|
|
def __rshift__(self, other):
|
2008-06-10 20:26:20 -04:00
|
|
|
|
return int(self) >> int(other)
|
2007-07-31 17:14:18 -04:00
|
|
|
|
|
2007-08-01 13:11:55 -04:00
|
|
|
|
def __rrshift__(self, other):
|
2008-06-10 20:26:20 -04:00
|
|
|
|
return int(other) >> int(self)
|
2007-08-01 13:11:55 -04:00
|
|
|
|
|
2007-07-31 15:19:57 -04:00
|
|
|
|
def __and__(self, other):
|
2008-06-10 20:26:20 -04:00
|
|
|
|
return int(self) & int(other)
|
2007-07-31 17:14:18 -04:00
|
|
|
|
|
2007-08-01 13:11:55 -04:00
|
|
|
|
def __rand__(self, other):
|
2008-06-10 20:26:20 -04:00
|
|
|
|
return int(other) & int(self)
|
2007-08-01 13:11:55 -04:00
|
|
|
|
|
2007-07-31 15:19:57 -04:00
|
|
|
|
def __xor__(self, other):
|
2008-06-10 20:26:20 -04:00
|
|
|
|
return int(self) ^ int(other)
|
2007-07-31 17:14:18 -04:00
|
|
|
|
|
2007-08-01 13:11:55 -04:00
|
|
|
|
def __rxor__(self, other):
|
2008-06-10 20:26:20 -04:00
|
|
|
|
return int(other) ^ int(self)
|
2007-08-01 13:11:55 -04:00
|
|
|
|
|
2007-07-31 15:19:57 -04:00
|
|
|
|
def __or__(self, other):
|
2008-06-10 20:26:20 -04:00
|
|
|
|
return int(self) | int(other)
|
2007-07-31 15:19:57 -04:00
|
|
|
|
|
2007-08-01 13:11:55 -04:00
|
|
|
|
def __ror__(self, other):
|
2008-06-10 20:26:20 -04:00
|
|
|
|
return int(other) | int(self)
|
2007-08-01 13:11:55 -04:00
|
|
|
|
|
|
|
|
|
def __invert__(self):
|
2008-06-10 20:26:20 -04:00
|
|
|
|
return ~int(self)
|
2007-08-01 13:11:55 -04:00
|
|
|
|
|
2007-07-31 15:19:57 -04:00
|
|
|
|
# Concrete implementations of Rational and Real abstract methods.
|
|
|
|
|
def __float__(self):
|
2008-06-10 20:26:20 -04:00
|
|
|
|
"""float(self) == float(int(self))"""
|
2007-07-31 15:19:57 -04:00
|
|
|
|
return float(int(self))
|
2007-07-31 17:14:18 -04:00
|
|
|
|
|
2007-05-16 20:23:41 -04:00
|
|
|
|
@property
|
2007-07-31 15:19:57 -04:00
|
|
|
|
def numerator(self):
|
2008-06-10 20:26:20 -04:00
|
|
|
|
"""Integers are their own numerators."""
|
2007-12-06 12:49:44 -05:00
|
|
|
|
return +self
|
2007-07-31 17:14:18 -04:00
|
|
|
|
|
2007-05-16 20:23:41 -04:00
|
|
|
|
@property
|
2007-07-31 15:19:57 -04:00
|
|
|
|
def denominator(self):
|
2008-06-10 20:26:20 -04:00
|
|
|
|
"""Integers have a denominator of 1."""
|
2007-07-31 15:19:57 -04:00
|
|
|
|
return 1
|
2007-04-25 16:42:59 -04:00
|
|
|
|
|
2007-07-31 15:19:57 -04:00
|
|
|
|
|
2007-08-01 13:11:55 -04:00
|
|
|
|
Changes to operations and __magic__ methods
|
|
|
|
|
-------------------------------------------
|
|
|
|
|
|
|
|
|
|
To support more precise narrowing from float to int (and more
|
2007-12-06 12:49:44 -05:00
|
|
|
|
generally, from Real to Integral), we propose the following new
|
2007-08-01 13:11:55 -04:00
|
|
|
|
__magic__ methods, to be called from the corresponding library
|
|
|
|
|
functions. All of these return Integrals rather than Reals.
|
|
|
|
|
|
|
|
|
|
1. ``__trunc__(self)``, called from a new builtin ``trunc(x)``, which
|
|
|
|
|
returns the Integral closest to ``x`` between 0 and ``x``.
|
|
|
|
|
|
|
|
|
|
2. ``__floor__(self)``, called from ``math.floor(x)``, which returns
|
|
|
|
|
the greatest Integral ``<= x``.
|
|
|
|
|
|
|
|
|
|
3. ``__ceil__(self)``, called from ``math.ceil(x)``, which returns the
|
|
|
|
|
least Integral ``>= x``.
|
|
|
|
|
|
2007-08-22 18:07:53 -04:00
|
|
|
|
4. ``__round__(self)``, called from ``round(x)``, which returns the
|
2008-01-06 19:25:37 -05:00
|
|
|
|
Integral closest to ``x``, rounding half as the type chooses.
|
|
|
|
|
``float`` will change in 3.0 to round half toward even. There is
|
|
|
|
|
also a 2-argument version, ``__round__(self, ndigits)``, called
|
|
|
|
|
from ``round(x, ndigits)``, which should return a Real.
|
|
|
|
|
|
|
|
|
|
In 2.6, ``math.floor``, ``math.ceil``, and ``round`` will continue to
|
|
|
|
|
return floats.
|
2007-08-01 13:11:55 -04:00
|
|
|
|
|
2008-02-09 20:14:46 -05:00
|
|
|
|
The ``int()`` conversion implemented by ``float`` is equivalent to
|
|
|
|
|
``trunc()``. In general, the ``int()`` conversion should try
|
|
|
|
|
``__int__()`` first and if it is not found, try ``__trunc__()``.
|
2007-08-01 13:11:55 -04:00
|
|
|
|
|
2007-08-22 18:07:53 -04:00
|
|
|
|
``complex.__{divmod,mod,floordiv,int,float}__`` also go away. It would
|
|
|
|
|
be nice to provide a nice error message to help confused porters, but
|
|
|
|
|
not appearing in ``help(complex)`` is more important.
|
2007-08-01 13:11:55 -04:00
|
|
|
|
|
|
|
|
|
|
2007-07-31 15:19:57 -04:00
|
|
|
|
Notes for type implementors
|
|
|
|
|
---------------------------
|
|
|
|
|
|
|
|
|
|
Implementors should be careful to make equal numbers equal and
|
|
|
|
|
hash them to the same values. This may be subtle if there are two
|
|
|
|
|
different extensions of the real numbers. For example, a complex type
|
|
|
|
|
could reasonably implement hash() as follows::
|
2007-04-25 16:42:59 -04:00
|
|
|
|
|
2007-05-16 20:23:41 -04:00
|
|
|
|
def __hash__(self):
|
2007-07-31 15:19:57 -04:00
|
|
|
|
return hash(complex(self))
|
2007-04-25 16:42:59 -04:00
|
|
|
|
|
2007-07-31 15:19:57 -04:00
|
|
|
|
but should be careful of any values that fall outside of the built in
|
|
|
|
|
complex's range or precision.
|
2007-04-25 16:42:59 -04:00
|
|
|
|
|
2007-05-16 20:23:41 -04:00
|
|
|
|
Adding More Numeric ABCs
|
2007-08-01 13:11:55 -04:00
|
|
|
|
~~~~~~~~~~~~~~~~~~~~~~~~
|
2007-04-25 16:42:59 -04:00
|
|
|
|
|
2007-05-16 20:23:41 -04:00
|
|
|
|
There are, of course, more possible ABCs for numbers, and this would
|
|
|
|
|
be a poor hierarchy if it precluded the possibility of adding
|
|
|
|
|
those. You can add ``MyFoo`` between ``Complex`` and ``Real`` with::
|
2007-04-25 16:42:59 -04:00
|
|
|
|
|
2007-05-16 20:23:41 -04:00
|
|
|
|
class MyFoo(Complex): ...
|
|
|
|
|
MyFoo.register(Real)
|
2007-04-25 16:42:59 -04:00
|
|
|
|
|
2007-07-31 15:19:57 -04:00
|
|
|
|
Implementing the arithmetic operations
|
2007-08-01 13:11:55 -04:00
|
|
|
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
2007-07-31 15:19:57 -04:00
|
|
|
|
|
|
|
|
|
We want to implement the arithmetic operations so that mixed-mode
|
|
|
|
|
operations either call an implementation whose author knew about the
|
|
|
|
|
types of both arguments, or convert both to the nearest built in type
|
|
|
|
|
and do the operation there. For subtypes of Integral, this means that
|
|
|
|
|
__add__ and __radd__ should be defined as::
|
|
|
|
|
|
|
|
|
|
class MyIntegral(Integral):
|
2007-07-31 17:14:18 -04:00
|
|
|
|
|
2007-07-31 15:19:57 -04:00
|
|
|
|
def __add__(self, other):
|
|
|
|
|
if isinstance(other, MyIntegral):
|
|
|
|
|
return do_my_adding_stuff(self, other)
|
|
|
|
|
elif isinstance(other, OtherTypeIKnowAbout):
|
|
|
|
|
return do_my_other_adding_stuff(self, other)
|
|
|
|
|
else:
|
|
|
|
|
return NotImplemented
|
2007-07-31 17:14:18 -04:00
|
|
|
|
|
2007-07-31 15:19:57 -04:00
|
|
|
|
def __radd__(self, other):
|
|
|
|
|
if isinstance(other, MyIntegral):
|
|
|
|
|
return do_my_adding_stuff(other, self)
|
|
|
|
|
elif isinstance(other, OtherTypeIKnowAbout):
|
|
|
|
|
return do_my_other_adding_stuff(other, self)
|
|
|
|
|
elif isinstance(other, Integral):
|
|
|
|
|
return int(other) + int(self)
|
|
|
|
|
elif isinstance(other, Real):
|
|
|
|
|
return float(other) + float(self)
|
|
|
|
|
elif isinstance(other, Complex):
|
|
|
|
|
return complex(other) + complex(self)
|
|
|
|
|
else:
|
|
|
|
|
return NotImplemented
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
There are 5 different cases for a mixed-type operation on subclasses
|
|
|
|
|
of Complex. I'll refer to all of the above code that doesn't refer to
|
|
|
|
|
MyIntegral and OtherTypeIKnowAbout as "boilerplate". ``a`` will be an
|
|
|
|
|
instance of ``A``, which is a subtype of ``Complex`` (``a : A <:
|
|
|
|
|
Complex``), and ``b : B <: Complex``. I'll consider ``a + b``:
|
|
|
|
|
|
|
|
|
|
1. If A defines an __add__ which accepts b, all is well.
|
|
|
|
|
2. If A falls back to the boilerplate code, and it were to return
|
|
|
|
|
a value from __add__, we'd miss the possibility that B defines
|
|
|
|
|
a more intelligent __radd__, so the boilerplate should return
|
|
|
|
|
NotImplemented from __add__. (Or A may not implement __add__ at
|
|
|
|
|
all.)
|
|
|
|
|
3. Then B's __radd__ gets a chance. If it accepts a, all is well.
|
|
|
|
|
4. If it falls back to the boilerplate, there are no more possible
|
|
|
|
|
methods to try, so this is where the default implementation
|
|
|
|
|
should live.
|
|
|
|
|
5. If B <: A, Python tries B.__radd__ before A.__add__. This is
|
|
|
|
|
ok, because it was implemented with knowledge of A, so it can
|
|
|
|
|
handle those instances before delegating to Complex.
|
|
|
|
|
|
|
|
|
|
If ``A<:Complex`` and ``B<:Real`` without sharing any other knowledge,
|
|
|
|
|
then the appropriate shared operation is the one involving the built
|
|
|
|
|
in complex, and both __radd__s land there, so ``a+b == b+a``.
|
2007-04-25 16:42:59 -04:00
|
|
|
|
|
2007-05-16 20:23:41 -04:00
|
|
|
|
|
|
|
|
|
Rejected Alternatives
|
|
|
|
|
=====================
|
|
|
|
|
|
|
|
|
|
The initial version of this PEP defined an algebraic hierarchy
|
|
|
|
|
inspired by a Haskell Numeric Prelude [#numericprelude]_ including
|
|
|
|
|
MonoidUnderPlus, AdditiveGroup, Ring, and Field, and mentioned several
|
2007-12-06 12:49:44 -05:00
|
|
|
|
other possible algebraic types before getting to the numbers. We had
|
2007-05-16 20:23:41 -04:00
|
|
|
|
expected this to be useful to people using vectors and matrices, but
|
2007-07-31 15:19:57 -04:00
|
|
|
|
the NumPy community really wasn't interested, and we ran into the
|
|
|
|
|
issue that even if ``x`` is an instance of ``X <: MonoidUnderPlus``
|
|
|
|
|
and ``y`` is an instance of ``Y <: MonoidUnderPlus``, ``x + y`` may
|
|
|
|
|
still not make sense.
|
|
|
|
|
|
2007-12-06 12:49:44 -05:00
|
|
|
|
Then we gave the numbers a much more branching structure to include
|
2007-07-31 15:19:57 -04:00
|
|
|
|
things like the Gaussian Integers and Z/nZ, which could be Complex but
|
|
|
|
|
wouldn't necessarily support things like division. The community
|
|
|
|
|
decided that this was too much complication for Python, so I've now
|
|
|
|
|
scaled back the proposal to resemble the Scheme numeric tower much
|
|
|
|
|
more closely.
|
|
|
|
|
|
2007-04-25 16:42:59 -04:00
|
|
|
|
|
2008-02-09 20:14:46 -05:00
|
|
|
|
The Decimal Type
|
|
|
|
|
================
|
|
|
|
|
|
|
|
|
|
After consultation with its authors it has been decided that the
|
|
|
|
|
``Decimal`` type should not at this time be made part of the numeric
|
|
|
|
|
tower.
|
|
|
|
|
|
|
|
|
|
|
2007-04-25 16:42:59 -04:00
|
|
|
|
References
|
|
|
|
|
==========
|
|
|
|
|
|
2007-05-16 20:23:41 -04:00
|
|
|
|
.. [#pep3119] Introducing Abstract Base Classes
|
2007-04-25 16:42:59 -04:00
|
|
|
|
(http://www.python.org/dev/peps/pep-3119/)
|
|
|
|
|
|
2007-12-06 12:49:44 -05:00
|
|
|
|
.. [#classtree] Possible Python 3K Class Tree?, wiki page by Bill Janssen
|
2007-04-25 16:42:59 -04:00
|
|
|
|
(http://wiki.python.org/moin/AbstractBaseClasses)
|
|
|
|
|
|
2007-12-06 12:49:44 -05:00
|
|
|
|
.. [#numericprelude] NumericPrelude: An experimental alternative hierarchy
|
|
|
|
|
of numeric type classes
|
2007-04-25 16:42:59 -04:00
|
|
|
|
(http://darcs.haskell.org/numericprelude/docs/html/index.html)
|
|
|
|
|
|
2007-05-16 20:23:41 -04:00
|
|
|
|
.. [#schemetower] The Scheme numerical tower
|
|
|
|
|
(http://www.swiss.ai.mit.edu/ftpdir/scheme-reports/r5rs-html/r5rs_8.html#SEC50)
|
2007-04-25 16:42:59 -04:00
|
|
|
|
|
|
|
|
|
|
2007-05-16 20:23:41 -04:00
|
|
|
|
Acknowledgements
|
|
|
|
|
================
|
2007-04-25 16:42:59 -04:00
|
|
|
|
|
2007-09-04 04:21:25 -04:00
|
|
|
|
Thanks to Neal Norwitz for encouraging me to write this PEP in the
|
2007-05-16 20:23:41 -04:00
|
|
|
|
first place, to Travis Oliphant for pointing out that the numpy people
|
2007-07-31 15:19:57 -04:00
|
|
|
|
didn't really care about the algebraic concepts, to Alan Isaac for
|
|
|
|
|
reminding me that Scheme had already done this, and to Guido van
|
|
|
|
|
Rossum and lots of other people on the mailing list for refining the
|
|
|
|
|
concept.
|
2007-04-25 16:42:59 -04:00
|
|
|
|
|
|
|
|
|
Copyright
|
|
|
|
|
=========
|
|
|
|
|
|
|
|
|
|
This document has been placed in the public domain.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
..
|
|
|
|
|
Local Variables:
|
|
|
|
|
mode: indented-text
|
|
|
|
|
indent-tabs-mode: nil
|
|
|
|
|
sentence-end-double-space: t
|
|
|
|
|
fill-column: 70
|
|
|
|
|
coding: utf-8
|
|
|
|
|
End:
|