In this guide, you'll explore Python's numbers module, which defines numeric abstract base classes. Learn its features and examples for type hierarchy.
The numbers
module in Python provides a hierarchy of abstract base classes (ABCs) to define and test numeric types. This module is useful for creating your own numeric types and ensuring that they conform to the behavior expected of numbers.
Table of Contents
- Introduction
- Key Classes in
numbers
Number
Complex
Real
Rational
Integral
- Examples
- Checking Numeric Types
- Creating Custom Numeric Types
- Real-World Use Case
- Conclusion
- References
Introduction
The numbers
module defines a set of abstract base classes that describe the various categories of numbers in Python. These categories range from the most general, Number
, to the most specific, Integral
. These classes provide a way to define numeric types in a consistent manner and to check if an object is of a particular numeric type.
Key Classes in numbers
Number
The most general ABC for numbers. All other numeric ABCs are derived from this class. It is not intended to be instantiated directly.
import numbers
print(isinstance(5, numbers.Number)) # True
print(isinstance(3.14, numbers.Number)) # True
Output:
True
True
Complex
Represents complex numbers. All numbers in Python (including integers and floats) are complex numbers.
import numbers
print(isinstance(5, numbers.Complex)) # True
print(isinstance(3.14, numbers.Complex)) # True
print(isinstance(1+2j, numbers.Complex)) # True
Output:
True
True
True
Real
Represents real numbers. This includes both integers and floating-point numbers, but not complex numbers.
import numbers
print(isinstance(5, numbers.Real)) # True
print(isinstance(3.14, numbers.Real)) # True
print(isinstance(1+2j, numbers.Real)) # False
Output:
True
True
False
Rational
Represents rational numbers. These are numbers that can be expressed as the quotient of two integers. In Python, this includes integers and fractions.
from fractions import Fraction
import numbers
print(isinstance(5, numbers.Rational)) # True
print(isinstance(Fraction(1, 3), numbers.Rational)) # True
print(isinstance(3.14, numbers.Rational)) # False
Output:
True
True
False
Integral
Represents integral numbers. These are whole numbers, including both positive and negative integers, as well as zero.
import numbers
print(isinstance(5, numbers.Integral)) # True
print(isinstance(-3, numbers.Integral)) # True
print(isinstance(3.14, numbers.Integral)) # False
Output:
True
True
False
Examples
Checking Numeric Types
Use the isinstance
function to check if a value is an instance of a particular numeric type.
import numbers
print(isinstance(5, numbers.Number)) # True
print(isinstance(5, numbers.Complex)) # True
print(isinstance(5, numbers.Real)) # True
print(isinstance(5, numbers.Rational)) # True
print(isinstance(5, numbers.Integral)) # True
print(isinstance(3.14, numbers.Integral)) # False
Output:
True
True
True
True
True
False
Creating Custom Numeric Types
You can create custom numeric types by subclassing one of the abstract base classes provided by the numbers
module.
import numbers
class MyNumber(numbers.Real):
def __init__(self, value):
self.value = value
def __repr__(self):
return f"MyNumber({self.value})"
def __float__(self):
return float(self.value)
def __int__(self):
return int(self.value)
def __hash__(self):
return hash(self.value)
def __trunc__(self):
return int(self.value)
def __floor__(self):
return int(self.value // 1)
def __ceil__(self):
return int(-(-self.value // 1))
def __round__(self, ndigits=None):
return MyNumber(round(self.value, ndigits))
def __add__(self, other):
if isinstance(other, MyNumber):
return MyNumber(self.value + other.value)
return MyNumber(self.value + other)
def __sub__(self, other):
if isinstance(other, MyNumber):
return MyNumber(self.value - other.value)
return MyNumber(self.value - other)
def __mul__(self, other):
if isinstance(other, MyNumber):
return MyNumber(self.value * other.value)
return MyNumber(self.value * other)
def __truediv__(self, other):
if isinstance(other, MyNumber):
return MyNumber(self.value / other.value)
return MyNumber(self.value / other)
def __floordiv__(self, other):
if isinstance(other, MyNumber):
return MyNumber(self.value // other.value)
return MyNumber(self.value // other)
def __mod__(self, other):
if isinstance(other, MyNumber):
return MyNumber(self.value % other.value)
return MyNumber(self.value % other)
def __pow__(self, exponent):
if isinstance(exponent, MyNumber):
return MyNumber(self.value ** exponent.value)
return MyNumber(self.value ** exponent)
def __abs__(self):
return MyNumber(abs(self.value))
def __neg__(self):
return MyNumber(-self.value)
def __pos__(self):
return MyNumber(+self.value)
def __eq__(self, other):
if isinstance(other, MyNumber):
return self.value == other.value
return self.value == other
def __lt__(self, other):
if isinstance(other, MyNumber):
return self.value < other.value
return self.value < other
def __le__(self, other):
if isinstance(other, MyNumber):
return self.value <= other.value
return self.value <= other
def __gt__(self, other):
if isinstance(other, MyNumber):
return self.value > other.value
return self.value > other
def __ge__(self, other):
if isinstance(other, MyNumber):
return self.value >= other.value
return self.value >= other
def __radd__(self, other):
return self.__add__(other)
def __rsub__(self, other):
return MyNumber(other).__sub__(self)
def __rmul__(self, other):
return self.__mul__(other)
def __rtruediv__(self, other):
return MyNumber(other).__truediv__(self)
def __rfloordiv__(self, other):
return MyNumber(other).__floordiv__(self)
def __rmod__(self, other):
return MyNumber(other).__mod__(self)
def __rpow__(self, other):
return MyNumber(other).__pow__(self)
# Example usage
a = MyNumber(3)
b = MyNumber(4)
print(a + b) # MyNumber(7)
print(a * b) # MyNumber(12)
print(a ** 2) # MyNumber(9)
print(abs(MyNumber(-5))) # MyNumber(5)
print(a + 2) # MyNumber(5)
print(2 + a) # MyNumber(5)
print(a // 2) # MyNumber(1)
print(a % 2) # MyNumber(1)
print(2 // a) # MyNumber(0)
print(2 % a) # MyNumber(2)
print(round(a, 1)) # MyNumber(3)
Output:
MyNumber(7)
MyNumber(12)
MyNumber(9)
MyNumber(5)
MyNumber(5)
MyNumber(5)
MyNumber(1)
MyNumber(1)
MyNumber(0)
MyNumber(2)
MyNumber(3)
Real-World Use Case
Custom Numeric Operations
In scientific computing or financial applications, you might need custom numeric types that adhere to specific rules or provide additional functionality. Using the numbers
module, you can create these custom numeric types while ensuring they conform to the expected behavior of standard numeric types.
import numbers
class Currency(numbers.Real):
def __init__(self, value):
self.value = value
def __repr__(self):
return f"Currency({self.value:.2f})"
def __float__(self):
return float(self.value)
def __int__(self):
return int(self.value)
def __hash__(self):
return hash(self.value)
def __trunc__(self):
return int(self.value)
def __floor__(self):
return int(self.value // 1)
def __ceil__(self):
return int(-(-self.value // 1))
def __round__(self, ndigits=None):
return Currency(round(self.value, ndigits))
def __add__(self, other):
if isinstance(other, numbers.Real):
return Currency(self.value + float(other))
return NotImplemented
def __sub__(self, other):
if isinstance(other, numbers.Real):
return Currency(self.value - float(other))
return NotImplemented
def __mul__(self, other):
if isinstance(other, numbers.Real):
return Currency(self.value * float(other))
return NotImplemented
def __truediv__(self, other):
if isinstance(other, numbers.Real):
return Currency(self.value / float(other))
return NotImplemented
def __floordiv__(self, other):
if isinstance(other, numbers.Real):
return Currency(self.value // float(other))
return NotImplemented
def __mod__(self, other):
if isinstance(other, numbers.Real):
return Currency(self.value % float(other))
return NotImplemented
def __pow__(self, exponent):
if isinstance(exponent, numbers.Real):
return Currency(self.value ** float(exponent))
return NotImplemented
def __abs__(self):
return Currency(abs(self.value))
def __neg__(self):
return Currency(-self.value)
def __pos__(self):
return Currency(+self.value)
def __eq__(self, other):
if isinstance(other, numbers.Real):
return self.value == float(other)
return NotImplemented
def __lt__(self, other):
if isinstance(other, numbers.Real):
return self.value < float(other)
return NotImplemented
def __le__(self, other):
if isinstance(other, numbers.Real):
return self.value <= float(other)
return NotImplemented
def __gt__(self, other):
if isinstance(other, numbers.Real):
return self.value > float(other)
return NotImplemented
def __ge__(self, other):
if isinstance(other, numbers.Real):
return self.value >= float(other)
return NotImplemented
def __radd__(self, other):
return self.__add__(other)
def __rsub__(self, other):
return Currency(float(other)).__sub__(self)
def __rmul__(self, other):
return self.__mul__(other)
def __rtruediv__(self, other):
return Currency(float(other)).__truediv__(self)
def __rfloordiv__(self, other):
return Currency(float(other)).__floordiv__(self)
def __rmod__(self, other):
return Currency(float(other)).__mod__(self)
def __rpow__(self, other):
return Currency(float(other)).__pow__(self)
# Example usage
salary = Currency(5000.75)
bonus = Currency(1200.50)
total_income = salary + bonus
print(total_income) # Currency(6201.25)
Output:
Currency(6201.25)
Conclusion
The numbers
module in Python provides a robust framework for defining and working with numeric types. By using the abstract base classes in this module, you can ensure that your custom numeric types adhere to the expected behaviors of standard numeric types, making your code more consistent and reliable.
Comments
Post a Comment
Leave Comment