68

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?

sorin
  • 161,544
  • 178
  • 535
  • 806

4 Answers4

126

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.

Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
21

Python 2 supports four types for numbers int,float, long and complexand 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
Ashwini Chaudhary
  • 244,495
  • 58
  • 464
  • 504
4

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.

Community
  • 1
  • 1
dshepherd
  • 4,989
  • 4
  • 39
  • 46
  • I know this is really old but ... `thing + 1` doesn't raise a TypeError if `thing` is a boolean. Also if thing is `numpy.nan` then the function returns True, which might not make sense. – Toby Petty May 07 '19 at 21:51
  • I think accepting nans is reasonable: although it's short for "not a number" it's a still a float, which is a numeric type. Most strongly-typed languages that I know of will compile with nans in places that accept numbers. It sucks that operator+ is defined for booleans though, I didn't know that. – dshepherd May 08 '19 at 09:50
  • 1
    ... for sure `thing +1` will work, and won't raise any exception if `thing` is any _numpy_ or _scipy_ array, which I doubt is the desired behaviour, if one wants to trap if `var` is list-like... – lurix66 Nov 05 '19 at 08:59
3

One can use the numbers module from the The Python Standard Library:

# import numbers
isinstance(var, numbers.Number)

tells if var is a number. Examples:

import numbers
var = 5  ; print(isinstance(var, numbers.Number)) # True
var = 5.5; print(isinstance(var, numbers.Number)) # True
var = 'a'; print(isinstance(var, numbers.Number)) # False

import numpy as np
var = np.float128(888); print(isinstance(var, numbers.Number)) # True

class C: pass; var = C(); print(isinstance(c, numbers.Number)) # False
S.V
  • 2,149
  • 2
  • 18
  • 41
  • This is true, but note that the OP wanted their code to work with Python 2.5, and the numbers module was introduced in 2.6. – snakecharmerb May 09 '22 at 16:40
  • 2
    As time goes on, it will become progressively less and less likely that someone would be searching here for an answer working specifically with Python 2.5. People will need answers working with more recent versions of python. – S.V May 09 '22 at 21:44
  • This is also true, but: (1) There is an [existing Q&A ](https://stackoverflow.com/q/3441358/5320906) for the general case (2) Using `numbers` is already proposed in the [accepted answer](https://stackoverflow.com/a/11204870/5320906) to this Q&A, so this answer is redundant and should be deleted. – snakecharmerb May 10 '22 at 05:28