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


114

给定一个任意的python对象,确定它是否为数字的最佳方法是什么?这里is定义为acts like a number in certain circumstances

例如,假设您正在编写向量类。如果给定另一个向量,则要查找点积。如果给出标量,则要缩放整个矢量。

检查,如果事情是intfloatlongbool很烦人,不包括可能像数字用户定义的对象。但是,__mul__例如,检查并不够好,因为我刚刚描述的向量类将定义__mul__,但它不是我想要的那种类型。

Answers:


135

使用Numbernumbers模块测试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

当然,这与鸭子的打字相反。如果你更关心的对象如何行为,而不是它什么,执行您的操作,如果你有一个号码,使用异常,否则告诉你。


3
将向量乘以X时,最好做聪明的事情,而不要做鸭子的事情。在这种情况下,您想根据X 什么来做不同的事情。(它可能起到事半功倍的作用,但结果可能并不明智。)
Evgeni Sergeev 2015年

3
这个答案会说True是一个数字..可能并不总是您想要的。对于排除布尔值(认为验证fe),我想说isinstance(value, Number) and type(value) != bool
Yo Ludke '17

32

您要检查是否有物体

在某些情况下像数字一样

如果您使用的是Python 2.5或更早版本,则唯一的真实方法是检查某些“特定情况”并查看。

在2.6或更好的,你可以使用isinstancenumbers.Number -一个抽象基类(ABC)存在正是为了这个目的(其它更多的ABC中存在的collections模块为各种形式的集合/容器,重新开始与2.6;以及同样仅在这些发行版中,如果需要,您可以轻松地添加自己的抽象基类。

0在某些情况下,Bach达到2.5或更早版本时,“可以添加但不能迭代”可能是一个很好的定义。但是,您确实需要问自己,您要问的是,您要考虑的“数字”一定一定能够做的,而它绝对不能做的是什么,然后检查。

在2.6或更高版本中也可能需要这样做,也许是出于进行自己的注册以添加您尚未注册的您所关心的类型的目的numbers.Numbers-如果您想排除某些声称其为数字的类型,但是您只是无法处理,这需要更多的注意,因为ABC没有unregister方法[[例如,您可以制作自己的ABC WeirdNum并在其中注册所有此类怪异类型,然后isinstance在继续进行之前先检查其保释金检查isinstance正常numbers.Number是否继续成功。

顺便说一句,是否以及何时需要检查是否x可以做某事,通常必须尝试以下操作:

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

__add__本身的存在告诉您没有什么用处,因为例如所有序列都具有将其与其他序列连接的目的。例如,此检查等效于定义“数字是某种东西,使得这样的事物的序列是内置函数的有效单个参数sum”。完全怪异的类型(例如,总和为0时引发“错误的”异常的类型,例如a ZeroDivisionErrorValueError&c)将传播异常,但这没关系,让用户尽快知道这样的疯狂类型根本不能接受公司;-); 但是,一个可乘以标量的“向量”(Python的标准库没有),但是在这里它当然是作为第三方扩展而流行的),在这里也会给出错误的结果,因此(例如“不允许迭代”(例如,检查是否iter(x)加注TypeError,或者是否存在特殊方法__iter__-如果您的年龄在2.5或更早,因此需要您自己进行检查)。

简要了解一下此类复杂性可能足以激励您在可行的情况下改为依赖抽象基类。


但是在数字模块中有一个数字的ABC。这就是文档所宣称的:“数字模块(PEP 3141)定义了数字抽象基类的层次结构,该层次结构逐渐定义了更多操作。”
史蒂文·鲁姆巴尔斯基

17

这是一个例外真正发光的好例子。只需执行对数字类型的处理,然后TypeError从其他所有类型中捕获即可。

但是显然,这只会检查操作是否有效,而不是是否有意义!唯一真正的解决方案是永远不要混合类型,并且始终确切地知道您的值属于什么类型类。


1
鸭打字+1:我的数据是什么类型都无所谓,只是我是否可以用它做我想做的事情。
systempuntoout 2010年

12
这是传统的做法,但基本知识已经在相当一部分被引入到获得从单纯的鸭打字和走向世界移动一段距离这里isinstance其实是可以有用在许多情况下(==“检查是有意义的”,以及正式的适用性操作)。对于长期使用Python的人们来说,这是艰难的转变,但是在Python哲学中一个非常重要的微妙趋势是,忽略它将是一个严重的错误。
亚历克斯·马丁里

@Alex:是的,我喜欢打字课(主要是collections.Sequence朋友)。但是afaik,没有针对数字,向量或任何其他数学对象的此类类。
Jochen Ritzel 2010年

1
没有反对鸭打字。这就是我会做的。但是,有一个数字的抽象基类:numbers.Number。
史蒂芬·鲁姆巴尔斯基

4

将对象乘以零。任何数字乘以零就是零。其他任何结果均表示该对象不是数字(包括异常)

def isNumber(x):
    try:
        return bool(0 == x*0)
    except:
        return False

因此,使用isNumber将给出以下输出:

class A: pass 

def foo(): return 1

for x in [1,1.4, A(), range(10), foo, foo()]:
    answer = isNumber(x)
    print('{answer} == isNumber({x})'.format(**locals()))

输出:

True == isNumber(1)
True == isNumber(1.4)
False == isNumber(<__main__.A instance at 0x7ff52c15d878>)
False == isNumber([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
False == isNumber(<function foo at 0x7ff52c121488>)
True == isNumber(1)

世界上可能有一些非数字对象定义__mul__为乘以零时返回零,但这是一个极端的例外。该解决方案应涵盖您生成/诱骗的所有正常代码和健全代码。

numpy.array示例:

import numpy as np

def isNumber(x):
    try:
        return bool(x*0 == 0)
    except:
        return False

x = np.array([0,1])

answer = isNumber(x)
print('{answer} == isNumber({x})'.format(**locals()))

输出:

False == isNumber([0 1])

5
True * 0 == 0
endolith'7

4
您的函数将错误地说布尔值是数字
endolith

1
@endolith,布尔运算的作用与数字完全相同。始终为True总是== 1,始终为False = =0。这恰恰是发问者要求的,“这里'是'被定义为'在某些情况下就像数字一样的行为'。”
shrewmouse17年

1
@endolith,实际上,布尔值是数字。布尔值源自于此,int因此我的函数将正确地说布尔值是数字。
shrewmouse17年

1
@NicolasAbril,将0 * x == 0转换为isNumber中的布尔值。
shrewmouse

3

为了改写您的问题,您正在尝试确定某物是集合还是单个值。试图比较某物是矢量还是数字,就是将苹果与橘子进行比较-我可以使用字符串或数字的矢量,也可以使用单个字符串或数字。您对拥有的数量(1个或更多)感兴趣,而不是实际的类型感兴趣

我对此问题的解决方案是通过检查是否存在来检查输入是单个值还是集合__len__。例如:

def do_mult(foo, a_vector):
    if hasattr(foo, '__len__'):
        return sum([a*b for a,b in zip(foo, a_vector)])
    else:
        return [foo*b for b in a_vector]

或者,对于鸭子输入法,您可以先尝试迭代foo

def do_mult(foo, a_vector):
    try:
        return sum([a*b for a,b in zip(foo, a_vector)])
    except TypeError:
        return [foo*b for b in a_vector]

最终,测试某事物是否类似于矢量比测试某事物是否类似于标量要容易。如果您遇到不同类型的值(例如,字符串,数字等),那么程序的逻辑可能需要做些工作-您最终是如何尝试将字符串乘以数字向量的?


3

总结/评估现有方法:

Candidate    | type                      | delnan | mat | shrewmouse | ant6n
-------------------------------------------------------------------------
0            | <type 'int'>              |      1 |   1 |          1 |     1
0.0          | <type 'float'>            |      1 |   1 |          1 |     1
0j           | <type 'complex'>          |      1 |   1 |          1 |     0
Decimal('0') | <class 'decimal.Decimal'> |      1 |   0 |          1 |     1
True         | <type 'bool'>             |      1 |   1 |          1 |     1
False        | <type 'bool'>             |      1 |   1 |          1 |     1
''           | <type 'str'>              |      0 |   0 |          0 |     0
None         | <type 'NoneType'>         |      0 |   0 |          0 |     0
'0'          | <type 'str'>              |      0 |   0 |          0 |     1
'1'          | <type 'str'>              |      0 |   0 |          0 |     1
[]           | <type 'list'>             |      0 |   0 |          0 |     0
[1]          | <type 'list'>             |      0 |   0 |          0 |     0
[1, 2]       | <type 'list'>             |      0 |   0 |          0 |     0
(1,)         | <type 'tuple'>            |      0 |   0 |          0 |     0
(1, 2)       | <type 'tuple'>            |      0 |   0 |          0 |     0

(我是通过这个问题来到这里的)

#!/usr/bin/env python

"""Check if a variable is a number."""

import decimal


def delnan_is_number(candidate):
    import numbers
    return isinstance(candidate, numbers.Number)


def mat_is_number(candidate):
    return isinstance(candidate, (int, long, float, complex))


def shrewmouse_is_number(candidate):
    try:
        return 0 == candidate * 0
    except:
        return False


def ant6n_is_number(candidate):
    try:
        float(candidate)
        return True
    except:
        return False

# Test
candidates = (0, 0.0, 0j, decimal.Decimal(0),
              True, False, '', None, '0', '1', [], [1], [1, 2], (1, ), (1, 2))

methods = [delnan_is_number, mat_is_number, shrewmouse_is_number, ant6n_is_number]

print("Candidate    | type                      | delnan | mat | shrewmouse | ant6n")
print("-------------------------------------------------------------------------")
for candidate in candidates:
    results = [m(candidate) for m in methods]
    print("{:<12} | {:<25} | {:>6} | {:>3} | {:>10} | {:>5}"
          .format(repr(candidate), type(candidate), *results))

为自己准备的待办事项:float('nan'), 'nan', '123.45', '42', '42a', '0x8', '0xa',加上math.isnan
Martin Thoma,

2

最好以相反的方式进行操作:检查它是否是向量。如果是,则进行点积运算,在所有其他情况下,将尝试进行标量乘法。

检查向量很容易,因为它应该是向量类类型(或从其继承)。您也可以先尝试做一个点积,如果失败了(=它实际上不是一个向量),然后退回到标量乘法。


1

只是为了补充。也许我们可以如下结合使用isinstance和isdigit来确定值是否为数字(int,float等)

如果isinstance(num1,int)或isinstance(num1,float)或num1.isdigit():


0

对于假设的向量类:

假设v是一个向量,我们将其乘以x。如果是有意义的繁衍每个组件v通过x,我们或许意味着,所以尝试,第一。如果没有,也许我们可以点吗?否则是类型错误。

编辑 -以下代码不起作用,因为2*[0]==[0,0]而不是引发TypeError。我将其保留,因为它已被评论。

def __mul__( self, x ):
    try:
        return [ comp * x for comp in self ]
    except TypeError:
        return [ x * y for x, y in itertools.zip_longest( self, x, fillvalue = 0 )

如果x是一个向量,然后[comp * x for comp in self]将产生的外积xv。这是2级张量,不是标量。
aaronasterling

将“非标量”更改为“非向量”。至少不在原始向量空间中。
aaronasterling

嘿,实际上我们都是错的。你假定comp*x将规模xcomp,我是假设,这将引发TypeError。不幸的是,它实际上会xcomp时间联系在一起。哎呀。
卡特里尔(Katriel)2010年

嗯 如果x是向量,则它应具有__rmul__方法(__rmul__ = __mul__),以便comp * x应按明显打算的x方式进行 缩放x * comp
aaronasterling

0

在实现某种矢量类时,我遇到了类似的问题。检查数字的一种方法是只转换为一个,即使用

float(x)

这应该拒绝x不能转换为数字的情况;但也可能会拒绝其他可能有效的类似数字的结构,例如复数。


0

如果要根据参数类型调用不同的方法,请查看multipledispatch

例如,假设您正在编写向量类。如果给定另一个向量,则要查找点积。如果给出标量,则要缩放整个矢量。

from multipledispatch import dispatch

class Vector(list):

    @dispatch(object)
    def __mul__(self, scalar):
        return Vector( x*scalar for x in self)

    @dispatch(list)
    def __mul__(self, other):
        return sum(x*y for x,y in zip(self, other))


>>> Vector([1,2,3]) * Vector([2,4,5])   # Vector time Vector is dot product
25
>>> Vector([1,2,3]) * 2                 # Vector times scalar is scaling
[2, 4, 6]

不幸的是,(据我所知)我们无法编写代码,@dispatch(Vector)因为我们仍在定义type Vector,因此尚未定义类型名称。相反,我使用的是基类型list,它甚至允许您找到a Vector和a 的点积list


0

简短的方法:

obj = 12345
print(isinstance(obj,int))

输出:

True

如果对象是字符串,则将返回'False':

obj = 'some string'
print(isinstance(obj,int))

输出:

False

0

您有一个数据项,说rec_day当写入文件时将是一个float。但程序处理期间,可以是floatintstr类型(str初始化一个新的记录时被使用并且包含一个伪标记的值)。

然后,您可以检查一下是否有此号码

                type(rec_day) != str 

我已经以这种方式构造了一个python程序,然后将其作为数字检查放入“维护补丁”中。这是Python方式吗?很可能没有,因为我以前使用COBOL编程。


-1

您可以使用isdigit()函数。

>>> x = "01234"
>>> a.isdigit()
True
>>> y = "1234abcd"
>>> y.isdigit()
False

“ 01234”不是数字,它是一个字符串。
阿列克谢
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.