检查对象是否为数字的最优雅方法是什么?

149 投票
16 回答
42603 浏览
提问于 2025-04-16 02:32

给定一个任意的Python对象,怎么判断它是不是一个数字呢?这里的“是”指的是“在某些情况下表现得像数字”。

举个例子,假设你在写一个向量类。如果给你另一个向量,你想计算它们的点积;如果给你一个标量(就是普通的数字),你想把整个向量缩放一下。

单单检查一个东西是不是 int(整数)、float(浮点数)、long(长整型)、bool(布尔值)这些类型,太麻烦了,而且还不能涵盖那些用户自己定义的、可能表现得像数字的对象。不过,光检查 __mul__(乘法运算符)也不够,因为我刚才提到的向量类会定义 __mul__,但它并不是我想要的那种数字。

16 个回答

17

这是一个很好的例子,展示了异常处理的优势。你只需要像处理数字类型一样,捕捉其他操作中出现的 TypeError 错误。

不过,显然,这样做只是检查一个操作是否能正常工作,并不能判断它是否合理!解决这个问题的唯一办法就是不要混用不同类型的数据,始终清楚你的值属于哪种类型。

34

你想要检查某个对象在特定情况下是否像数字一样工作。

如果你使用的是 Python 2.5 或更早的版本,唯一的方法就是检查一些“特定情况”,然后看看结果。

在 2.6 或更新的版本中,你可以使用 isinstancenumbers.Number,这是一个专门为这个目的设计的抽象基类(ABC)。在 collections 模块中还有很多其他的 ABC,用于各种集合/容器类型,从 2.6 开始就有了;而且在这些版本中,如果你需要的话,还可以轻松添加自己的抽象基类。

回到 2.5 及更早版本,“可以加到 0 上并且不可迭代”在某些情况下可能是一个不错的定义。但是,你真的需要问自己,你想要认为“数字”的东西必须能够什么,以及它绝对不能做什么——然后进行检查。

在 2.6 或更高版本中,这可能也是必要的,尤其是当你想要注册一些你关心的类型,而这些类型还没有被注册到 numbers.Number 时。如果你想要排除一些声称是数字但你无法处理的类型,那就需要更加小心,因为 ABC 没有 unregister 方法。例如,你可以创建自己的 ABC WeirdNum,并在其中注册所有你认为奇怪的类型,然后首先检查 isinstance,如果不符合条件就退出,再继续检查正常的 numbers.Number

顺便说一下,如果你需要检查 x 是否能做某件事,通常你需要尝试以下操作:

try: 0 + x
except TypeError: canadd=False
else: canadd=True

单单检查 __add__ 并没有提供有用的信息,因为例如所有序列都有这个方法,用于与其他序列连接。这个检查相当于定义“数字是指这样的东西:一系列这样的东西可以作为内置函数 sum 的有效单一参数”。例如,完全奇怪的类型(比如在与 0 相加时抛出“错误”异常的类型,比如 ZeroDivisionErrorValueError 等)会传播异常,但这没关系,尽快让用户知道这些奇怪的类型在正常情况下是不可接受的;不过,一个可以加到标量的“向量”(Python 的标准库没有这个,但当然它们作为第三方扩展很受欢迎)在这里也会给出错误的结果,所以(例如)这个检查应该在“不可迭代”的检查之后进行(例如,检查 iter(x) 是否抛出 TypeError,或者检查是否存在特殊方法 __iter__——如果你在 2.5 或更早版本中,因此需要自己进行检查)。

简单了解这些复杂情况可能会激励你在可行的情况下更依赖于抽象基类……;-)

167

使用来自numbers模块的Number来检查一个东西是不是数字,可以用isinstance(n, Number)这个方法(从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

当然,这和鸭子类型是相反的。鸭子类型的意思是,你更关注一个东西的行为而不是它的本质。所以,如果你觉得某个东西像数字一样,就可以直接用它进行操作,如果它不是数字,程序会通过错误提示你。

撰写回答