I found some old Python code that was doing something like:
if type(var) is type(1):
...
As expected, pep8
complains about this recommending usage of isinstance()
.
Now, the problem is that the numbers
module was added in Python 2.6 and I need to write code that works with Python 2.5+
So if isinstance(var, Numbers.number)
is not a solution.
Which would be the proper solution in this case?
In Python 2, you can use the types
module:
>>> import types
>>> var = 1
>>> NumberTypes = (types.IntType, types.LongType, types.FloatType, types.ComplexType)
>>> isinstance(var, NumberTypes)
True
Note the use of a tuple to test against multiple types.
Under the hood, IntType
is just an alias for int
, etc.:
>>> isinstance(var, (int, long, float, complex))
True
The complex
type requires that your python was compiled with support for complex numbers; if you want to guard for this use a try/except block:
>>> try:
... NumberTypes = (types.IntType, types.LongType, types.FloatType, types.ComplexType)
... except AttributeError:
... # No support for complex numbers compiled
... NumberTypes = (types.IntType, types.LongType, types.FloatType)
...
or if you just use the types directly:
>>> try:
... NumberTypes = (int, long, float, complex)
... except NameError:
... # No support for complex numbers compiled
... NumberTypes = (int, long, float)
...
In Python 3 types
no longer has any standard type aliases, complex
is always enabled and there is no longer a long
vs int
difference, so in Python 3 always use:
NumberTypes = (int, float, complex)
Last but not least, you can use the numbers.Numbers
abstract base type (new in Python 2.6) to also support custom numeric types that don't derive directly from the above types:
>>> import numbers
>>> isinstance(var, numbers.Number)
True
This check also returns True
for decimal.Decimal()
and fractions.Fraction()
objects.
This module does make the assumption that the complex
type is enabled; you'll get an import error if it is not.
Python 2 supports four types for numbers int
,float
, long
and complex
and python 3.x
supports 3:int
, float
and complex
>>> num = 10
>>> if isinstance(num, (int, float, long, complex)): #use tuple if checking against multiple types
print('yes it is a number')
yes it is a number
>>> isinstance(num, float)
False
>>> isinstance(num, int)
True
>>> a = complex(1, 2)
>>> isinstance(a, complex)
True
Depending on what you're using this in duck typing could be a better approach (it's certainly commonly recommended). The problem with Martijn Pieters' approach is that you will always miss some types of number from your list. Off the top of my head your code won't work with: sympy rational numbers, arbitrary precision integers and any implementation of complex numbers.
One alternative is to write a function like this:
def is_number(thing):
try:
thing + 1
return True
except TypeError:
return False
This code should work with any reasonable implementation of a number. Of course there is a major downside: it will also work with an unreasonable implementation of plenty of non-numbers (i.e. if the plus operator is overloaded and accepts an integer).
Another alternative (depending on why you need to know if something is a number) is to just assume it is a number, and if it isn't errors will be thrown by whichever bit of the code requires a number.
I'm not saying these approaches are always better (unlike some people...) just that they are worth considering.