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

时间:2010-08-09 15:04:27

标签: python types numbers

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

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

检查某些内容是intfloatlongbool是否令人讨厌,并且不包含可能与数字相同的用户定义对象。但是,例如,检查__mul__是不够好的,因为我刚才描述的矢量类会定义__mul__,但它不会是我想要的那种数字。

15 个答案:

答案 0 :(得分:114)

使用Number模块中的numbers来测试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 '%15s %s' % (n.__repr__(), isinstance(n, Number))
              2 True
            2.0 True
 Decimal('2.0') True
         (2+0j) True
 Fraction(2, 1) True
            '2' False

这当然与鸭子打字相反。如果您更关心对象的行为方式而不是 ,请执行您的操作,就好像您有一个数字并使用异常来告诉您。

答案 1 :(得分:29)

您想要检查是否有某个对象

  

在某些情况下就像一个数字   情况

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

在2.6或更高版本中,您可以将isinstancenumbers.Number一起使用 - 一个完全为此目的而存在的抽象基类(ABC)(collections模块中存在更多ABCs对于各种形式的集合/容器,再次从2.6开始;并且,只有在那些版本中,您可以根据需要轻松添加自己的抽象基类。

巴赫到2.5及更早, “可以添加到0并且不可迭代”在某些情况下可能是一个很好的定义。但, 你真的需要问问自己,你问的是什么你想要考虑的“数字”必须绝对能够,它必须绝对是无法< / strong>要做 - 并检查。

在2.6或更高版本中也可能需要这样做,也许是为了让您自己的注册添加您尚未注册的类型numbers.Numbers - 如果您想排除一些声称它们是数字但你无法处理的类型,这需要更加小心,因为ABC没有unregister方法[[例如你可以制作自己的ABC] {{1并注册所有这些奇怪的类型,然后在继续检查正常WeirdNum的{​​{1}}以继续成功之前,首先检查其isinstance以进行纾困。

顺便说一下,如果您需要检查isinstance是否可以做某事,您通常需要尝试以下方式:

numbers.Number

x本身的存在告诉你没什么用处,因为例如所有序列都有它以便与其他序列连接。例如,此检查等同于“数字就是这样的事物的序列是内置函数try: 0 + x except TypeError: canadd=False else: canadd=True 的有效单个参数”。完全奇怪的类型(例如,当总和为0时引发“错误”异常的类型,例如,__add__sum&amp; c)将传播异常,但是没关系,让用户尽快知道这种疯狂的类型在好公司中是不可接受的;-);但是,一个可以与标量相加的“向量”(Python的标准库没有一个,但当然它们作为第三方扩展很受欢迎)也会在这里给出错误的结果,所以(例如)这个检查应该来了< em>在之后“不允许迭代”(例如,检查ZeroDivisionError是否会引发ValueError,还是存在特殊方法iter(x) - 如果你'重新在2.5或更早,因此需要你自己的检查。)

对这些并发症的简要介绍可能足以激励您在可行时依赖抽象基类......; - )。

答案 2 :(得分:16)

这是一个很好的例子,异常真正闪耀。只需按照数字类型执行操作,然后从其他所有内容中捕获TypeError

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

答案 3 :(得分:4)

将对象乘以零。任何数字零都为零。任何其他结果意味着该对象不是数字(包括例外)

def isNumber(x):
    try:
        return 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__在乘以零时返回零,但这是一个极端的例外。此解决方案应涵盖您生成/加密的所有普通理智代码。

答案 4 :(得分: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]

最终,测试某些东西是否像矢量一样比测试某些东西是否是标量更容易。如果您有不同类型的值(即字符串,数字等),那么程序的逻辑可能需要一些工作 - 您最初是如何尝试将字符串乘以数字向量的? / p>

答案 5 :(得分:2)

可能反过来做的更好:你检查它是否是一个向量。如果是,则执行点积,在所有其他情况下,您尝试标量乘法。

检查向量很简单,因为它应该是矢量类类型(或从它继承)。你也可以先尝试做一个点积,如果失败了(=它实际上不是矢量),那么就回到标量乘法。

答案 6 :(得分:2)

总结/评估现有方法:

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

(我来自this question

代码

#!/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))

答案 7 :(得分:1)

只是补充一下。 也许我们可以使用isinstance和isdigit的组合来查找值是否为数字(int,float等)

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

答案 8 :(得分: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 )

答案 9 :(得分:0)

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

float(x)

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

答案 10 :(得分: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),因此我们无法编写Vector,因此尚未定义类型名称。相反,我使用的是基本类型list,它甚至允许您找到Vectorlist的点积。

答案 11 :(得分:0)

简单快捷的方法:

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

输出:

True

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

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

输出:

False

答案 12 :(得分:0)

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

然后您可以检查一下是否有这个号码

                type(rec_day) != str 

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

答案 13 :(得分:0)

可以在一个简单的try异常块中实现

def check_if_number(str1):
    try:
        int(float(str1))
        return 'number'
    except:
        return 'not a number'

a = check_if_number('32322')
print (a)
# number

答案 14 :(得分:-1)

您可以使用isdigit()函数。

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