如何正确使用Python中的isinstance()来检查变量是否为数字?

68

我找到了一些旧的Python代码,它做的事情类似于:

if type(var) is type(1):
   ...
按预期,`pep8` 对此提出了抱怨,建议使用 `isinstance()`。但是问题在于 `numbers` 模块从 Python 2.6 才开始存在,而我需要编写能够与 Python 2.5+ 兼容的代码。因此 `if isinstance(var, Numbers.number)` 并非解决方案。在这种情况下,什么才是正确的解决方案呢?

1
如果你愿意使用numpy,numpy.isfinite 应该可以解决问题。 - Jon Warneke
4个回答

126

在Python 2中,您可以使用types模块

>>> import types
>>> var = 1
>>> NumberTypes = (types.IntType, types.LongType, types.FloatType, types.ComplexType)
>>> isinstance(var, NumberTypes)
True

注意使用元组来测试多种类型。

在底层,IntType 只是 int 的别名,等等:

>>> isinstance(var, (int, long, float, complex))
True

complex类型要求你的Python编译时支持复数;如果你想进行防范,可以使用try/except语句块:

>>> 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)
...
或者如果您直接使用这些类型:
>>> try:
...     NumberTypes = (int, long, float, complex)
... except NameError:
...     # No support for complex numbers compiled
...     NumberTypes = (int, long, float)
...
在Python 3中,types不再具有任何标准类型别名,complex始终启用,不再有longint之间的差异,因此在Python 3中始终使用:
NumberTypes = (int, float, complex)

最后但并非最不重要的,您可以使用numbers.Numbers抽象基础类型(自Python 2.6起新增)来支持不直接继承上述类型的自定义数值类型:

>>> import numbers
>>> isinstance(var, numbers.Number)
True

检查函数同时也返回 decimal.Decimal()fractions.Fraction() 对象的值为True

这个模块默认假设complex类型是可用的;如果不可用则会导致导入错误。


2
似乎 numpy.int32 没有被 int 捕获。 - Matthias Arras
@MatthiasArras,它不是Python int类型的子类。但是isinstance(np.int32(32), numbers.Integral)是正确的。 - Martijn Pieters

21

Python 2支持4种数字类型intfloatlongcomplex,而Python 3.x只支持3种类型:intfloatcomplex

>>> 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

6

可以使用Python标准库中的numbers模块:

# import numbers
isinstance(var, numbers.Number)

判断var是否为数字。示例:

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

这是正确的,但请注意OP希望他们的代码能够在Python 2.5中运行,而numbers模块是在2.6中引入的。 - snakecharmerb
5
随着时间的推移,越来越不可能有人在这里寻找特定使用Python 2.5的答案。人们需要使用更新版本的Python的答案。 - S.V
这也是正确的,但是:(1) 对于一般情况已经有一个现有的问答 (2) 在这个Q&A中,使用numbers已经被提出并被接受作为答案,因此这个回答是多余的,应该被删除。 - snakecharmerb

4

根据您使用的情况,鸭子类型可能是更好的方法(它肯定 常见 推荐)。Martijn Pieters的方法存在问题,您的列表中总会漏掉某些数字类型。就我所知,您的代码将无法处理:sympy有理数、任意精度整数以及任何实现复数的实现。

另一种选择是编写此类函数:

def is_number(thing):
    try:
        thing + 1
        return True
    except TypeError:
        return False

这段代码应该适用于任何合理的数字实现。当然,有一个主要缺点:它也可以与大量非数字的不合理实现一起使用(即如果加号运算符被重载并接受整数)。

另一个选择(取决于您需要知道某个东西是否为数字的原因)是假设它是数字,如果不是,则在需要数字的任何代码位处将引发错误。

我并不是说这些方法总是更好(不像有些人...),只是值得考虑。


我知道这很老了,但是...如果thing是布尔值,thing + 1不会引发TypeError。另外,如果thingnumpy.nan,那么该函数将返回True,这可能没有意义。 - Toby Petty
我认为接受NaN是合理的:尽管它是“不是数字”的缩写,但它仍然是浮点数,这是一种数值类型。我所知道的大多数强类型语言都会在接受数字的地方编译NaN。不过,operator+也适用于布尔值,这真的很糟糕,我之前并不知道。 - dshepherd
1
毫无疑问,如果“thing”是任何_numpy_或_scipy_数组,则thing +1将起作用,并且不会引发任何异常,我怀疑这不是期望的行为,如果想要捕获是否为类似列表的“var”。 - lurix66

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接