What is the most pythonic way to check if an object is a number?
Use Number
from the numbers
module to test isinstance(n, Number)
(available since 2.6).
>>> from numbers import Number
... from decimal import Decimal
... from fractions import Fraction
... for n in [2, 2.0, Decimal('2.0'), complex(2, 0), Fraction(2, 1), '2']:
... print(f'{n!r:>14} {isinstance(n, Number)}')
2 True
2.0 True
Decimal('2.0') True
(2+0j) True
Fraction(2, 1) True
'2' False
This is, of course, contrary to duck typing. If you are more concerned about how an object acts rather than what it is, perform your operations as if you have a number and use exceptions to tell you otherwise.
You want to check if some object
acts like a number in certain circumstances
If you're using Python 2.5 or older, the only real way is to check some of those "certain circumstances" and see.
In 2.6 or better, you can use isinstance
with numbers.Number -- an abstract base class (ABC) that exists exactly for this purpose (lots more ABCs exist in the collections
module for various forms of collections/containers, again starting with 2.6; and, also only in those releases, you can easily add your own abstract base classes if you need to).
Bach to 2.5 and earlier,
"can be added to 0
and is not iterable" could be a good definition in some cases. But,
you really need to ask yourself, what it is that you're asking that what you want to consider "a number" must definitely be able to do, and what it must absolutely be unable to do -- and check.
This may also be needed in 2.6 or later, perhaps for the purpose of making your own registrations to add types you care about that haven't already be registered onto numbers.Numbers
-- if you want to exclude some types that claim they're numbers but you just can't handle, that takes even more care, as ABCs have no unregister
method [[for example you could make your own ABC WeirdNum
and register there all such weird-for-you types, then first check for isinstance
thereof to bail out before you proceed to checking for isinstance
of the normal numbers.Number
to continue successfully.
BTW, if and when you need to check if x
can or cannot do something, you generally have to try something like:
try: 0 + x
except TypeError: canadd=False
else: canadd=True
The presence of __add__
per se tells you nothing useful, since e.g all sequences have it for the purpose of concatenation with other sequences. This check is equivalent to the definition "a number is something such that a sequence of such things is a valid single argument to the builtin function sum
", for example. Totally weird types (e.g. ones that raise the "wrong" exception when summed to 0, such as, say, a ZeroDivisionError
or ValueError
&c) will propagate exception, but that's OK, let the user know ASAP that such crazy types are just not acceptable in good company;-); but, a "vector" that's summable to a scalar (Python's standard library doesn't have one, but of course they're popular as third party extensions) would also give the wrong result here, so (e.g.) this check should come after the "not allowed to be iterable" one (e.g., check that iter(x)
raises TypeError
, or for the presence of special method __iter__
-- if you're in 2.5 or earlier and thus need your own checks).
A brief glimpse at such complications may be sufficient to motivate you to rely instead on abstract base classes whenever feasible...;-).
This is a good example where exceptions really shine. Just do what you would do with the numeric types and catch the TypeError
from everything else.
But obviously, this only checks if a operation works, not whether it makes sense! The only real solution for that is to never mix types and always know exactly what typeclass your values belong to.