It depends how thorough you want to be. Besides the builtin types (complex
, float
, and int
) there are also other types that are considered numbers in python. For instance: fractions.Fraction
, decimal.Decimal
, and even bool
can act as a number. Then you get external libraries that have their own numeric types. By far the biggest is numpy
. With numpy
some of its types will succeed isinstance
checks, and other will not. For instance: isinstance(numpy.float64(10), float)
is true, but isinstance(numpy.float32(10), float)
is not.
On top of all this you could even have a user defined class that acts like a number.
Python does provide one way of getting around this -- the numbers
module. It provides several abstract types that represent different types of numbers. Any class that implements numeric functionality can register itself as being compatible with the relevant types. numbers.Number
is the most basic, and therefore the one you're looking for. All you have to do is use it in your isinstance
checks. eg.
from numbers import Number
from decimal import Decimal
from fractions import Fraction
import numpy
assert isinstance(1, Number)
assert isinstance(1.5, Number)
assert isinstance(1+5j, Number)
assert isinstance(True, Number)
assert isinstance(Decimal("1.23"), Number)
assert isinstance(Fraction(1, 2), Number)
assert isinstance(numpy.float64(10), Number)
assert isinstance(numpy.float32(10), Number)
assert isinstance(numpy.int32(10), Number)
assert isinstance(numpy.uint32(10), Number)
That still leaves us with the problem about whether the object is actually a number, rather than "not a number". The math.isnan
function is good for this, but it requires that the number be convertible to a float (which not all numbers are). The big problem here is the complex
type. There are a few ways around this: additional isinstance
checks (but that comes with its own headaches), using abs
, or testing for equality.
abs
can be used on every numeric type (that I can think of). For most numbers it returns the positive version of the number, but for complex numbers it returns its magnitude (a float). So now we can do that isnan
check. nan
is also a special number in that it is the only number that is not equal to itself.
This means your final check might look like:
import math
import numbers
def number_is_not_nan(n):
return isinstance(n, numbers.Number) and not math.isnan(abs(n))
def number_is_finite(n):
return isinstance(n, numbers.Number) and not math.isfinite(abs(n))