简体   繁体   English

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

[英]What is the most pythonic way to check if an object is a number?

Given an arbitrary python object, what's the best way to determine whether it is a number?给定一个任意的 python 对象,确定它是否为数字的最佳方法是什么? Here is is defined as acts like a number in certain circumstances .这里is被定义为acts like a number in certain circumstances

For example, say you are writing a vector class.例如,假设您正在编写矢量类。 If given another vector, you want to find the dot product.如果给定另一个向量,您想找到点积。 If given a scalar, you want to scale the whole vector.如果给定一个标量,您想缩放整个向量。

Checking if something is int , float , long , bool is annoying and doesn't cover user-defined objects that might act like numbers.检查某物是否为intfloatlongbool很烦人,并且不涵盖可能像数字一样行为的用户定义对象。 But, checking for __mul__ , for example, isn't good enough because the vector class I just described would define __mul__ , but it wouldn't be the kind of number I want.但是,例如检查__mul__还不够好,因为我刚才描述的向量类会定义__mul__ ,但它不是我想要的那种数字。

Use Number from the numbers module to test isinstance(n, Number) (available since 2.6).使用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

This is, of course, contrary to duck typing.当然,这与鸭子打字相反。 If you are more concerned about how an object acts rather than what it is , perform your operations as if you have a number and use exceptions to tell you otherwise.如果您更关心一个对象的行为方式而不是它什么,那么就好像您有一个数字一样执行您的操作,并使用异常来告诉您其他情况。

You want to check if some object你想检查一些对象

acts like a number in certain circumstances在某些情况下就像一个数字

If you're using Python 2.5 or older, the only real way is to check some of those "certain circumstances" and see.如果您使用的是 Python 2.5 或更早版本,唯一真正的方法是检查其中一些“特定情况”并查看。

In 2.6 or better, you can use isinstance with numbers.Number -- an abstract base class (ABC) that exists exactly for this purpose (lots more ABCs exist in the collections module for various forms of collections/containers, again starting with 2.6; and, also only in those releases, you can easily add your own abstract base classes if you need to).在 2.6 或更高版本中,您可以将isinstancenumbers.Number一起使用——一个抽象基类 (ABC) 正是为此目的而存在的(在collections模块中存在更多的 ABC,用于各种形式的集合/容器,同样从 2.6 开始;而且,也只有在这些版本中,您可以根据需要轻松添加自己的抽象基类)。

Bach to 2.5 and earlier, "can be added to 0 and is not iterable" could be a good definition in some cases. Bach 到 2.5 及更早版本,“可以添加到0且不可迭代”在某些情况下可能是一个很好的定义。 But, you really need to ask yourself, what it is that you're asking that what you want to consider "a number" must definitely be able to do , and what it must absolutely be unable to do -- and check.但是,你真的需要问问自己,你在问什么是你想考虑的“数字”必须绝对能够做到的,以及它绝对不能做的 - 并检查。

This may also be needed in 2.6 or later, perhaps for the purpose of making your own registrations to add types you care about that haven't already be registered onto numbers.Numbers -- if you want to exclude some types that claim they're numbers but you just can't handle, that takes even more care, as ABCs have no unregister method [[for example you could make your own ABC WeirdNum and register there all such weird-for-you types, then first check for isinstance thereof to bail out before you proceed to checking for isinstance of the normal numbers.Number to continue successfully.这可能在 2.6 或更高版本中也需要,也许是为了让您自己注册以添加您关心的尚未注册到数字上的类型。 numbers.Numbers - 如果您想排除某些声称它们是的类型数字,但你无法处理,这需要更加小心,因为 ABC 没有unregister方法[[例如,你可以创建自己的 ABC WeirdNum并在那里注册所有这些对你来说很奇怪的类型,然后首先检查它的isinstance在继续检查正常numbers.Numberisinstance之前退出。号码继续成功。

BTW, if and when you need to check if x can or cannot do something, you generally have to try something like:顺便说一句,如果以及何时需要检查x是否可以做某事或不能做某事,您通常必须尝试以下操作:

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

The presence of __add__ per se tells you nothing useful, since eg all sequences have it for the purpose of concatenation with other sequences. __add__的存在本身并没有告诉你任何有用的信息,因为例如所有序列都将它用于与其他序列连接的目的。 This check is equivalent to the definition "a number is something such that a sequence of such things is a valid single argument to the builtin function sum ", for example.例如,此检查等同于定义“一个数字是这样的东西,这样的东西的序列是内置函数sum的有效单个参数”。 Totally weird types (eg ones that raise the "wrong" exception when summed to 0, such as, say, a ZeroDivisionError or ValueError &c) will propagate exception, but that's OK, let the user know ASAP that such crazy types are just not acceptable in good company;-);完全奇怪的类型(例如,当总和为 0 时引发“错误”异常的类型,例如ZeroDivisionErrorValueError &c)将传播异常,但这没关系,让用户尽快知道这种疯狂的类型是不可接受的在好公司;-); but, a "vector" that's summable to a scalar (Python's standard library doesn't have one, but of course they're popular as third party extensions) would also give the wrong result here, so (eg) this check should come after the "not allowed to be iterable" one (eg, check that iter(x) raises TypeError , or for the presence of special method __iter__ -- if you're in 2.5 or earlier and thus need your own checks).但是,一个可以和标量相加的“向量”(Python 的标准库没有,但当然它们作为第三方扩展很受欢迎)也会在这里给出错误的结果,所以(例如)这个检查应该“不允许迭代”的(例如,检查iter(x)是否引发TypeError ,或者是否存在特殊方法__iter__ ——如果您使用的是 2.5 或更早版本,因此需要您自己的检查)。

A brief glimpse at such complications may be sufficient to motivate you to rely instead on abstract base classes whenever feasible...;-).对这些复杂情况的简要了解可能足以激励您在可行的情况下转而依赖抽象基类......;-)。

This is a good example where exceptions really shine.这是异常真正闪耀的一个很好的例子。 Just do what you would do with the numeric types and catch the TypeError from everything else.只需按照您对数字类型执行的操作进行处理,并从其他所有类型中捕获TypeError

But obviously, this only checks if a operation works , not whether it makes sense .但很明显,这只是检查操作是否有效,而不是它是否有意义 The only real solution for that is to never mix types and always know exactly what typeclass your values belong to.唯一真正的解决方案是永远不要混合类型,并且始终确切地知道您的值属于哪个类型类。

Multiply the object by zero.将对象乘以零。 Any number times zero is zero.任何数字乘以零都是零。 Any other result means that the object is not a number (including exceptions)任何其他结果表示该对象不是数字(包括例外)

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

Using isNumber thusly will give the following output:因此使用 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()))

Output:输出:

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)

There probably are some non-number objects in the world that define __mul__ to return zero when multiplied by zero but that is an extreme exception.世界上可能有一些非数字对象将__mul__定义为在乘以零时返回零,但这是一个极端的例外。 This solution should cover all normal and sane code that you generate/encouter.该解决方案应涵盖您生成/遇到的所有正常合理的代码。

numpy.array example: 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()))

output:输出:

False == isNumber([0 1])

To rephrase your question, you are trying to determine whether something is a collection or a single value.要改写您的问题,您正在尝试确定某物是集合还是单个值。 Trying to compare whether something is a vector or a number is comparing apples to oranges - I can have a vector of strings or numbers, and I can have a single string or single number.试图比较某物是向量还是数字就像是将苹果与橙子进行比较——我可以有一个字符串或数字的向量,也可以有一个字符串或一个数字。 You are interested in how many you have (1 or more) , not what type you actually have.您感兴趣的是您拥有多少(1 个或更多) ,而不是您实际拥有的类型。

my solution for this problem is to check whether the input is a single value or a collection by checking the presence of __len__ .我对这个问题的解决方案是通过检查__len__的存在来检查输入是单个值还是集合。 For example:例如:

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]

Or, for the duck-typing approach, you can try iterating on foo first:或者,对于 duck-typing 方法,您可以先尝试迭代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]

Ultimately, it is easier to test whether something is vector-like than to test whether something is scalar-like.最终,测试某事物是否类似于矢量比测试某事物是否类似于标量更容易。 If you have values of different type (ie string, numeric, etc.) coming through, then the logic of your program may need some work - how did you end up trying to multiply a string by a numeric vector in the first place?如果您有不同类型的值(即字符串、数字等)通过,那么您的程序逻辑可能需要一些工作 - 您最终是如何尝试将字符串乘以数字向量的?

To summarize / evaluate existing methods:总结/评估现有方法:

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

(I came here by this question ) (我是通过这个问题来到这里的)

Code代码

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

Probably it's better to just do it the other way around: You check if it's a vector.可能最好反过来做:检查它是否是向量。 If it is, you do a dot product and in all other cases you attempt scalar multiplication.如果是,则执行点积,在所有其他情况下尝试标量乘法。

Checking for the vector is easy, since it should of your vector class type (or inherited from it).检查矢量很容易,因为它应该属于您的矢量类类型(或从它继承)。 You could also just try first to do a dot-product, and if that fails (= it wasn't really a vector), then fall back to scalar multiplication.您也可以先尝试做一个点积,如果失败了(= 它不是真正的向量),然后回退到标量乘法。

Just to add upon.只是为了补充。 Perhaps we can use a combination of isinstance and isdigit as follows to find whether a value is a number (int, float, etc)也许我们可以像下面这样使用 isinstance 和 isdigit 的组合来判断一个值是否是一个数字(int、float 等)

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

can be implemented in a simple try exception block可以在一个简单的 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

I had a similar issue, when implementing a sort of vector class.在实现一种矢量类时,我遇到了类似的问题。 One way to check for a number is to just convert to one, ie by using检查数字的一种方法是只转换为一个,即使用

float(x)

This should reject cases where x cannot be converted to a number;这应该拒绝 x 不能转换为数字的情况; but may also reject other kinds of number-like structures that could be valid, for example complex numbers.但也可能拒绝其他类型的可能有效的类似数字的结构,例如复数。

For the hypothetical vector class:对于假设的向量类:

Suppose v is a vector, and we are multiplying it by x .假设v是一个向量,我们将它乘以x If it makes sense to multiply each component of v by x , we probably meant that, so try that first.如果将v的每个分量乘以x是有意义的,我们可能就是这个意思,所以先尝试一下。 If not, maybe we can dot?如果没有,也许我们可以打点? Otherwise it's a type error.否则就是类型错误。

EDIT -- the below code doesn't work, because 2*[0]==[0,0] instead of raising a TypeError .编辑——下面的代码不起作用,因为2*[0]==[0,0]而不是引发TypeError I leave it because it was commented-upon.我离开它是因为它被评论了。

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 )

If you want to call different methods depending on the argument type(s), look into multipledispatch .如果您想根据参数类型调用不同的方法,请查看multipledispatch

For example, say you are writing a vector class.例如,假设您正在编写矢量类。 If given another vector, you want to find the dot product.如果给定另一个向量,您想找到点积。 If given a scalar, you want to scale the whole vector.如果给定一个标量,您想缩放整个向量。

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]

Unfortunately, (to my knowledge) we can't write @dispatch(Vector) since we are still defining the type Vector , so that type name is not yet defined.不幸的是,(据我所知)我们不能写@dispatch(Vector)因为我们仍在定义类型Vector ,所以类型名称尚未定义。 Instead, I'm using the base type list , which allows you to even find the dot product of a Vector and a list .相反,我使用的是基本类型list ,它甚至可以让您找到Vectorlist的点积。

Short and simple way:简短的方法:

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

Output:输出:

True

If the object is a string, 'False' will be returned:如果对象是字符串,将返回“False”:

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

Output:输出:

False

You have a data item, say rec_day that when written to a file will be a float .你有一个数据项,比如rec_day ,当写入文件时将是一个float But during program processing it can be either float , int or str type (the str is used when initializing a new record and contains a dummy flag value).但在程序处理期间,它可以是floatintstr类型( str用于初始化新记录并包含一个虚拟标志值)。

You can then check to see if you have a number with this然后你可以检查你是否有这个号码

                type(rec_day) != str 

I've structured a python program this way and just put in 'maintenance patch' using this as a numeric check.我以这种方式构建了一个 python 程序,并使用它作为数字检查放入“维护补丁”。 Is it the Pythonic way?这是Pythonic方式吗? Most likely not since I used to program in COBOL.很可能不会,因为我以前用 COBOL 编程。

You can use numbers.Number to check if an object is a number.您可以使用numbers.Number来检查对象是否为数字。

For numbers, Python 3 supports 3 types int , float and complex types so if checking the 3 types of values with numbers.Number as shown below:对于数字, Python 3支持 3 种类型intfloat复杂类型,因此如果使用numbers.Number检查这 3 种类型的值,如下所示:

import numbers

print(type(100), isinstance(100, numbers.Number))
print(type(100.23), isinstance(100.23, numbers.Number))
print(type(100 + 2j), isinstance(100 + 2j, numbers.Number))

All return True as shown below:全部返回True如下图:

<class 'int'> True
<class 'float'> True
<class 'complex'> True

And, for numbers, Python 2 supperts 4 types int , long , float and complex types so if checking the 4 types of values with numbers.Number as shown below::而且,对于数字, Python 2支持 4 种类型intlongfloat复杂类型,因此如果使用numbers.Number检查 4 种类型的值,如下所示:

import numbers

print(type(100), isinstance(100, numbers.Number))
print(type(10000000000000000000), isinstance(10000000000000000000, numbers.Number))
print(type(100.23), isinstance(100.23, numbers.Number))
print(type(100 + 2j), isinstance(100 + 2j, numbers.Number))

All return True as shown below:全部返回True如下图:

(<type 'int'>, True)
(<type 'long'>, True)
(<type 'float'>, True)
(<type 'complex'>, True)

You could use the isdigit() function.您可以使用 isdigit() 函数。

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM