Numbers Module¶
The numbers module provides abstract base classes for numeric types, allowing you to check types and create numeric hierarchies.
Complexity Reference¶
| Operation | Time | Space | Notes |
|---|---|---|---|
isinstance(obj, Number) |
O(1) | O(1) | Type check |
issubclass(cls, Number) |
O(1) | O(1) | Class check |
register(cls) |
O(1) | O(1) | Register type |
Common Operations¶
Checking Numeric Types¶
import numbers
# O(1) - check if number (any numeric type)
value = 42
is_number = isinstance(value, numbers.Number) # True
# O(1) - check specific numeric types
is_integral = isinstance(value, numbers.Integral) # True
is_real = isinstance(value, numbers.Real) # True
is_complex = isinstance(value, numbers.Complex) # True
is_rational = isinstance(value, numbers.Rational) # True
# O(1) - works with all numeric types
print(isinstance(3.14, numbers.Real)) # True
print(isinstance(2+3j, numbers.Complex)) # True
print(isinstance(Decimal('1.5'), numbers.Real)) # True
Numeric Type Hierarchy¶
import numbers
from fractions import Fraction
from decimal import Decimal
# O(1) - check class hierarchy
print(issubclass(int, numbers.Integral)) # True
print(issubclass(float, numbers.Real)) # True
print(issubclass(complex, numbers.Complex)) # True
print(issubclass(Fraction, numbers.Rational)) # True
print(issubclass(Decimal, numbers.Real)) # False - not by default
# O(1) - type checking cascade
def process_number(value):
if isinstance(value, numbers.Integral):
return "Integer operation"
elif isinstance(value, numbers.Rational):
return "Rational operation"
elif isinstance(value, numbers.Real):
return "Real operation"
elif isinstance(value, numbers.Complex):
return "Complex operation"
return "Unknown"
result = process_number(Fraction(3, 4)) # O(1) - "Rational operation"
Common Use Cases¶
Creating Numeric Types¶
import numbers
class MyNumber(numbers.Number):
"""Custom numeric type - O(1)"""
def __init__(self, value):
self.value = value
def __add__(self, other):
if isinstance(other, numbers.Number): # O(1) check
return MyNumber(self.value + float(other))
return NotImplemented
def __mul__(self, other):
if isinstance(other, numbers.Number): # O(1) check
return MyNumber(self.value * float(other))
return NotImplemented
# Usage - O(1) type checks
num = MyNumber(5)
print(isinstance(num, numbers.Number)) # True
result = num + 3 # O(1) to check, O(1) to add
print(result.value) # 8
Function Argument Validation¶
import numbers
def calculate_average(values):
"""Calculate average with type validation - O(n)"""
# O(n) where n = number of values
total = 0
for value in values:
# O(1) type check per value
if not isinstance(value, numbers.Number):
raise TypeError(f"Expected number, got {type(value)}")
total += value
return total / len(values) if values else 0
# Usage - O(n)
result = calculate_average([1, 2, 3, 4, 5]) # O(5) = 3.0
result = calculate_average([1, 2, 'three']) # O(1) raises TypeError
Type-Safe Numeric Operations¶
import numbers
class Calculator:
"""Type-safe numeric calculator - O(1) per operation"""
def add(self, a, b):
"""Add two numbers with validation - O(1)"""
# O(1) to check types
if not isinstance(a, numbers.Number):
raise TypeError(f"a must be numeric, got {type(a)}")
if not isinstance(b, numbers.Number):
raise TypeError(f"b must be numeric, got {type(b)}")
# O(1) to perform operation
return a + b
def scale(self, value, factor):
"""Scale numeric value - O(1)"""
if not isinstance(value, numbers.Number):
raise TypeError("value must be numeric")
if not isinstance(factor, numbers.Real): # O(1)
raise TypeError("factor must be real")
# O(1) to multiply
return value * factor
# Usage - O(1) per operation
calc = Calculator()
result = calc.add(5, 3) # 8
result = calc.scale(result, 2) # 16
Polymorphic Numeric Functions¶
import numbers
from decimal import Decimal
from fractions import Fraction
def numeric_reciprocal(value):
"""Return reciprocal, preserving type - O(1)"""
# O(1) type check
if not isinstance(value, numbers.Number):
raise TypeError("value must be numeric")
# O(1) conditional based on type
if isinstance(value, numbers.Integral):
# Return as fraction to preserve precision
return Fraction(1, value)
elif isinstance(value, numbers.Rational):
return 1 / value
elif isinstance(value, numbers.Real):
return 1.0 / float(value)
else: # Complex
return 1 / value
# Usage - O(1) per call
print(numeric_reciprocal(2)) # Fraction(1, 2)
print(numeric_reciprocal(Fraction(3, 4))) # Fraction(4, 3)
print(numeric_reciprocal(2.5)) # 0.4
print(numeric_reciprocal(2+3j)) # (0.15384615384615385-0.23076923076923078j)
Constraint Validation¶
import numbers
def validate_range(value, min_val=None, max_val=None):
"""Validate numeric value is in range - O(1)"""
# O(1) type check
if not isinstance(value, numbers.Number):
raise TypeError("value must be numeric")
# O(1) comparisons (for most types)
if min_val is not None and value < min_val:
raise ValueError(f"value {value} < minimum {min_val}")
if max_val is not None and value > max_val:
raise ValueError(f"value {value} > maximum {max_val}")
return value
# Usage - O(1)
validate_range(5, 1, 10) # OK
validate_range(15, 1, 10) # Raises ValueError
Accepting Multiple Numeric Types¶
import numbers
from decimal import Decimal
from fractions import Fraction
def flexible_divide(numerator, denominator):
"""Accept any numeric types - O(1)"""
# O(1) type validation for each
if not isinstance(numerator, numbers.Number):
raise TypeError("numerator must be numeric")
if not isinstance(denominator, numbers.Number):
raise TypeError("denominator must be numeric")
# O(1) - Python handles type coercion
try:
return numerator / denominator
except ZeroDivisionError:
return None
# Usage - O(1) per call, works with any numeric type
print(flexible_divide(10, 2)) # 5.0
print(flexible_divide(Fraction(10, 3), 2)) # Fraction(5, 3)
print(flexible_divide(Decimal(10), 3)) # Decimal('3.333...')
print(flexible_divide(10+5j, 2)) # (5+2.5j)
Number Hierarchy¶
Number # Base - any number
├── Complex # Complex numbers
├── Real # Real numbers (no imaginary part)
│ └── Rational # Exact fractions
│ └── Integral # Whole numbers
Performance Tips¶
Cache Type Checks¶
import numbers
class NumberProcessor:
"""Cache type check results - O(1) lookup"""
def __init__(self):
self._type_cache = {}
def process(self, value):
"""O(1) cached type check"""
value_id = id(type(value))
if value_id not in self._type_cache:
# O(1) first time
self._type_cache[value_id] = self._determine_type(value)
return self._type_cache[value_id]
def _determine_type(self, value):
"""O(1) type determination"""
if isinstance(value, numbers.Integral):
return "integral"
elif isinstance(value, numbers.Rational):
return "rational"
elif isinstance(value, numbers.Real):
return "real"
else:
return "complex"
# Usage - O(1) after first call
processor = NumberProcessor()
for value in [1, 2, 3, 1.5, 2+3j]:
result = processor.process(value) # O(1) cached
Batch Type Checking¶
import numbers
def process_numbers(values):
"""Batch validate - O(n)"""
# O(n) validation
if not all(isinstance(v, numbers.Number) for v in values):
raise TypeError("All values must be numeric")
# O(n) processing
return sum(values) / len(values)
Version Notes¶
- Python 2.6+: numbers module available
- Python 3.x: All classes available
- Python 3.x: Decimal and Fraction support
Related Documentation¶
- Decimal Module - Precise decimal arithmetic
- Fractions Module - Rational numbers
- Math Module - Mathematical functions