mirror of
https://github.com/python/cpython.git
synced 2025-11-01 06:01:29 +00:00
* bpo-26680: Adds support for int.is_integer() for compatibility with float.is_integer(). The int.is_integer() method always returns True. * bpo-26680: Adds a test to ensure that False.is_integer() and True.is_integer() are always True. * bpo-26680: Adds Real.is_integer() with a trivial implementation using conversion to int. This default implementation is intended to reduce the workload for subclass implementers. It is not robust in the presence of infinities or NaNs and may have suboptimal performance for other types. * bpo-26680: Adds Rational.is_integer which returns True if the denominator is one. This implementation assumes the Rational is represented in it's lowest form, as required by the class docstring. * bpo-26680: Adds Integral.is_integer which always returns True. * bpo-26680: Adds tests for Fraction.is_integer called as an instance method. The tests for the Rational abstract base class use an unbound method to sidestep the inability to directly instantiate Rational. These tests check that everything works correct as an instance method. * bpo-26680: Updates documentation for Real.is_integer and built-ins int and float. The call x.is_integer() is now listed in the table of operations which apply to all numeric types except complex, with a reference to the full documentation for Real.is_integer(). Mention of is_integer() has been removed from the section 'Additional Methods on Float'. The documentation for Real.is_integer() describes its purpose, and mentions that it should be overridden for performance reasons, or to handle special values like NaN. * bpo-26680: Adds Decimal.is_integer to the Python and C implementations. The C implementation of Decimal already implements and uses mpd_isinteger internally, we just expose the existing function to Python. The Python implementation uses internal conversion to integer using to_integral_value(). In both cases, the corresponding context methods are also implemented. Tests and documentation are included. * bpo-26680: Updates the ACKS file. * bpo-26680: NEWS entries for int, the numeric ABCs and Decimal. Co-authored-by: Robert Smallshire <rob@sixty-north.com>
408 lines
11 KiB
Python
408 lines
11 KiB
Python
# Copyright 2007 Google, Inc. All Rights Reserved.
|
|
# Licensed to PSF under a Contributor Agreement.
|
|
|
|
"""Abstract Base Classes (ABCs) for numbers, according to PEP 3141.
|
|
|
|
TODO: Fill out more detailed documentation on the operators."""
|
|
|
|
from abc import ABCMeta, abstractmethod
|
|
|
|
__all__ = ["Number", "Complex", "Real", "Rational", "Integral"]
|
|
|
|
class Number(metaclass=ABCMeta):
|
|
"""All numbers inherit from this class.
|
|
|
|
If you just want to check if an argument x is a number, without
|
|
caring what kind, use isinstance(x, Number).
|
|
"""
|
|
__slots__ = ()
|
|
|
|
# Concrete numeric types must provide their own hash implementation
|
|
__hash__ = None
|
|
|
|
|
|
## Notes on Decimal
|
|
## ----------------
|
|
## Decimal has all of the methods specified by the Real abc, but it should
|
|
## not be registered as a Real because decimals do not interoperate with
|
|
## binary floats (i.e. Decimal('3.14') + 2.71828 is undefined). But,
|
|
## abstract reals are expected to interoperate (i.e. R1 + R2 should be
|
|
## expected to work if R1 and R2 are both Reals).
|
|
|
|
class Complex(Number):
|
|
"""Complex defines the operations that work on the builtin complex type.
|
|
|
|
In short, those are: a conversion to complex, .real, .imag, +, -,
|
|
*, /, abs(), .conjugate, ==, and !=.
|
|
|
|
If it is given heterogeneous arguments, and doesn't have special
|
|
knowledge about them, it should fall back to the builtin complex
|
|
type as described below.
|
|
"""
|
|
|
|
__slots__ = ()
|
|
|
|
@abstractmethod
|
|
def __complex__(self):
|
|
"""Return a builtin complex instance. Called for complex(self)."""
|
|
|
|
def __bool__(self):
|
|
"""True if self != 0. Called for bool(self)."""
|
|
return self != 0
|
|
|
|
@property
|
|
@abstractmethod
|
|
def real(self):
|
|
"""Retrieve the real component of this number.
|
|
|
|
This should subclass Real.
|
|
"""
|
|
raise NotImplementedError
|
|
|
|
@property
|
|
@abstractmethod
|
|
def imag(self):
|
|
"""Retrieve the imaginary component of this number.
|
|
|
|
This should subclass Real.
|
|
"""
|
|
raise NotImplementedError
|
|
|
|
@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
|
|
|
|
@abstractmethod
|
|
def __pos__(self):
|
|
"""+self"""
|
|
raise NotImplementedError
|
|
|
|
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 __truediv__(self, other):
|
|
"""self / other: Should promote to float when necessary."""
|
|
raise NotImplementedError
|
|
|
|
@abstractmethod
|
|
def __rtruediv__(self, other):
|
|
"""other / self"""
|
|
raise NotImplementedError
|
|
|
|
@abstractmethod
|
|
def __pow__(self, exponent):
|
|
"""self**exponent; should promote to float or complex when necessary."""
|
|
raise NotImplementedError
|
|
|
|
@abstractmethod
|
|
def __rpow__(self, base):
|
|
"""base ** self"""
|
|
raise NotImplementedError
|
|
|
|
@abstractmethod
|
|
def __abs__(self):
|
|
"""Returns the Real distance from 0. Called for abs(self)."""
|
|
raise NotImplementedError
|
|
|
|
@abstractmethod
|
|
def conjugate(self):
|
|
"""(x+y*i).conjugate() returns (x-y*i)."""
|
|
raise NotImplementedError
|
|
|
|
@abstractmethod
|
|
def __eq__(self, other):
|
|
"""self == other"""
|
|
raise NotImplementedError
|
|
|
|
Complex.register(complex)
|
|
|
|
|
|
class Real(Complex):
|
|
"""To Complex, Real adds the operations that work on real numbers.
|
|
|
|
In short, those are: a conversion to float, trunc(), divmod,
|
|
is_integer, %, <, <=, >, and >=.
|
|
|
|
Real also provides defaults for the derived operations.
|
|
"""
|
|
|
|
__slots__ = ()
|
|
|
|
@abstractmethod
|
|
def __float__(self):
|
|
"""Any Real can be converted to a native float object.
|
|
|
|
Called for float(self)."""
|
|
raise NotImplementedError
|
|
|
|
@abstractmethod
|
|
def __trunc__(self):
|
|
"""trunc(self): Truncates self to an Integral.
|
|
|
|
Returns an Integral i such that:
|
|
* i>0 iff self>0;
|
|
* abs(i) <= abs(self);
|
|
* for any Integral j satisfying the first two conditions,
|
|
abs(i) >= abs(j) [i.e. i has "maximal" abs among those].
|
|
i.e. "truncate towards 0".
|
|
"""
|
|
raise NotImplementedError
|
|
|
|
@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=None):
|
|
"""Rounds self to ndigits decimal places, defaulting to 0.
|
|
|
|
If ndigits is omitted or None, returns an Integral, otherwise
|
|
returns a Real. Rounds half toward even.
|
|
"""
|
|
raise NotImplementedError
|
|
|
|
def __divmod__(self, other):
|
|
"""divmod(self, other): The pair (self // other, self % other).
|
|
|
|
Sometimes this can be computed faster than the pair of
|
|
operations.
|
|
"""
|
|
return (self // other, self % other)
|
|
|
|
def __rdivmod__(self, other):
|
|
"""divmod(other, self): The pair (self // other, self % other).
|
|
|
|
Sometimes this can be computed faster than the pair of
|
|
operations.
|
|
"""
|
|
return (other // self, other % self)
|
|
|
|
@abstractmethod
|
|
def __floordiv__(self, other):
|
|
"""self // other: The floor() of self/other."""
|
|
raise NotImplementedError
|
|
|
|
@abstractmethod
|
|
def __rfloordiv__(self, other):
|
|
"""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):
|
|
"""self < other
|
|
|
|
< on Reals defines a total ordering, except perhaps for NaN."""
|
|
raise NotImplementedError
|
|
|
|
@abstractmethod
|
|
def __le__(self, other):
|
|
"""self <= other"""
|
|
raise NotImplementedError
|
|
|
|
def is_integer(self):
|
|
"""Return True if the Real is integral; otherwise return False.
|
|
|
|
This default implementation can be overridden in subclasses
|
|
for performance reasons or to deal with values such as NaN,
|
|
which would otherwise cause an exception to be raised.
|
|
"""
|
|
# Although __int__ is not defined at this level, the int
|
|
# constructor falls back to __trunc__, which we do have.
|
|
return self == int(self)
|
|
|
|
# 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):
|
|
"""Conjugate is a no-op for Reals."""
|
|
return +self
|
|
|
|
Real.register(float)
|
|
|
|
|
|
class Rational(Real):
|
|
""".numerator and .denominator should be in lowest terms."""
|
|
|
|
__slots__ = ()
|
|
|
|
@property
|
|
@abstractmethod
|
|
def numerator(self):
|
|
raise NotImplementedError
|
|
|
|
@property
|
|
@abstractmethod
|
|
def denominator(self):
|
|
raise NotImplementedError
|
|
|
|
# Concrete implementation of Real's conversion to float.
|
|
def __float__(self):
|
|
"""float(self) = self.numerator / self.denominator
|
|
|
|
It's important that this conversion use the integer's "true"
|
|
division rather than casting one side to float before dividing
|
|
so that ratios of huge integers convert without overflowing.
|
|
|
|
"""
|
|
return self.numerator / self.denominator
|
|
|
|
def is_integer(self):
|
|
"""Return True if the Rational is integral; otherwise return False."""
|
|
return self.denominator == 1
|
|
|
|
|
|
class Integral(Rational):
|
|
"""Integral adds a conversion to int and the bit-string operations."""
|
|
|
|
__slots__ = ()
|
|
|
|
@abstractmethod
|
|
def __int__(self):
|
|
"""int(self)"""
|
|
raise NotImplementedError
|
|
|
|
def __index__(self):
|
|
"""Called whenever an index is needed, such as in slicing"""
|
|
return int(self)
|
|
|
|
@abstractmethod
|
|
def __pow__(self, exponent, modulus=None):
|
|
"""self ** exponent % modulus, but maybe faster.
|
|
|
|
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
|
|
|
|
def is_integer(self):
|
|
"""Return True; all Integrals represent an integral value."""
|
|
return True
|
|
|
|
Integral.register(int)
|