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.