繁体   English   中英

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

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

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

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

检查某物是否为intfloatlongbool很烦人,并且不涵盖可能像数字一样行为的用户定义对象。 但是,例如检查__mul__还不够好,因为我刚才描述的向量类会定义__mul__ ,但它不是我想要的那种数字。

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

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

你想检查一些对象

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

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

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

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

这可能在 2.6 或更高版本中也需要,也许是为了让您自己注册以添加您关心的尚未注册到数字上的类型。 numbers.Numbers - 如果您想排除某些声称它们是的类型数字,但你无法处理,这需要更加小心,因为 ABC 没有unregister方法[[例如,你可以创建自己的 ABC WeirdNum并在那里注册所有这些对你来说很奇怪的类型,然后首先检查它的isinstance在继续检查正常numbers.Numberisinstance之前退出。号码继续成功。

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

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

__add__的存在本身并没有告诉你任何有用的信息,因为例如所有序列都将它用于与其他序列连接的目的。 例如,此检查等同于定义“一个数字是这样的东西,这样的东西的序列是内置函数sum的有效单个参数”。 完全奇怪的类型(例如,当总和为 0 时引发“错误”异常的类型,例如ZeroDivisionErrorValueError &c)将传播异常,但这没关系,让用户尽快知道这种疯狂的类型是不可接受的在好公司;-); 但是,一个可以和标量相加的“向量”(Python 的标准库没有,但当然它们作为第三方扩展很受欢迎)也会在这里给出错误的结果,所以(例如)这个检查应该“不允许迭代”的(例如,检查iter(x)是否引发TypeError ,或者是否存在特殊方法__iter__ ——如果您使用的是 2.5 或更早版本,因此需要您自己的检查)。

对这些复杂情况的简要了解可能足以激励您在可行的情况下转而依赖抽象基类......;-)。

这是异常真正闪耀的一个很好的例子。 只需按照您对数字类型执行的操作进行处理,并从其他所有类型中捕获TypeError

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

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

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

要改写您的问题,您正在尝试确定某物是集合还是单个值。 试图比较某物是向量还是数字就像是将苹果与橙子进行比较——我可以有一个字符串或数字的向量,也可以有一个字符串或一个数字。 您感兴趣的是您拥有多少(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]

或者,对于 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]

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

总结/评估现有方法:

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

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

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

只是为了补充。 也许我们可以像下面这样使用 isinstance 和 isdigit 的组合来判断一个值是否是一个数字(int、float 等)

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

可以在一个简单的 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

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

float(x)

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

对于假设的向量类:

假设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 )

如果您想根据参数类型调用不同的方法,请查看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的点积。

简短的方法:

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

输出:

True

如果对象是字符串,将返回“False”:

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

输出:

False

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

然后你可以检查你是否有这个号码

                type(rec_day) != str 

我以这种方式构建了一个 python 程序,并使用它作为数字检查放入“维护补丁”。 这是Pythonic方式吗? 很可能不会,因为我以前用 COBOL 编程。

您可以使用numbers.Number来检查对象是否为数字。

对于数字, 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))

全部返回True如下图:

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

而且,对于数字, 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))

全部返回True如下图:

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

您可以使用 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