问题:检查对象是否为数字的最有效方法是什么?
给定一个任意的python对象,确定它是否为数字的最佳方法是什么?这里is
定义为acts like a number in certain circumstances
。
例如,假设您正在编写向量类。如果给定另一个向量,则要查找点积。如果给出标量,则要缩放整个矢量。
检查,如果事情是int
,float
,long
,bool
很烦人,不包括可能像数字用户定义的对象。但是,__mul__
例如,检查并不够好,因为我刚刚描述的向量类将定义__mul__
,但它不是我想要的那种类型。
回答 0
使用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(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
当然,这与鸭子的打字相反。如果你更关心的对象如何行为,而不是它什么是,执行您的操作,如果你有一个号码,使用异常,否则告诉你。
回答 1
您要检查是否有物体
在某些情况下像数字一样
如果您使用的是Python 2.5或更早版本,则唯一的真实方法是检查某些“特定情况”并查看。
在2.6或更好的,你可以使用isinstance
与numbers.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 ZeroDivisionError
或ValueError
&c)将传播异常,但这没关系,让用户尽快知道这样的疯狂类型根本不能接受公司;-); 但是,一个可乘以标量的“向量”(Python的标准库没有),但是在这里它当然是作为第三方扩展而流行的),在这里也会给出错误的结果,因此(例如“不允许迭代”(例如,检查是否iter(x)
加注TypeError
,或者是否存在特殊方法__iter__
-如果您的年龄在2.5或更早,因此需要您自己进行检查)。
简要了解一下此类复杂性可能足以激励您在可行的情况下改为依赖抽象基类。
回答 2
这是一个exceptions真正发光的好例子。只需执行对数字类型的处理,然后TypeError
从其他所有类型中捕获即可。
但是显然,这只会检查操作是否有效,而不是是否有意义!唯一真正的解决方案是永远不要混合类型,并且始终确切地知道您的值属于什么类型类。
回答 3
将对象乘以零。任何数字乘以零就是零。其他任何结果均表示该对象不是数字(包括异常)
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__
为乘以零时返回零,但这是一个极端的exceptions。该解决方案应涵盖您生成/诱骗的所有正常代码和健全代码。
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])
回答 4
为了改写您的问题,您正在尝试确定某物是集合还是单个值。试图比较某物是矢量还是数字,就是将苹果与橘子进行比较-我可以使用字符串或数字的矢量,也可以使用单个字符串或数字。您对拥有的数量(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]
最终,测试某事物是否类似于矢量比测试某事物是否类似于标量要容易。如果您遇到不同类型的值(例如,字符串,数字等),那么程序的逻辑可能需要做些工作-您最终是如何尝试将字符串乘以数字向量的?
回答 5
总结/评估现有方法:
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))
回答 6
最好以相反的方式进行操作:检查它是否是向量。如果是,则进行点积运算,在所有其他情况下,将尝试进行标量乘法。
检查向量很容易,因为它应该是向量类类型(或从其继承)。您也可以先尝试做一个点积,如果失败了(=它实际上不是一个向量),然后退回到标量乘法。
回答 7
只是为了补充。也许我们可以如下结合使用isinstance和isdigit来确定值是否为数字(int,float等)
如果isinstance(num1,int)或isinstance(num1,float)或num1.isdigit():
回答 8
对于假设的向量类:
假设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
在实现某种矢量类时,我遇到了类似的问题。检查数字的一种方法是只转换为一个,即使用
float(x)
这应该拒绝x不能转换为数字的情况;但也可能会拒绝其他可能有效的类似数字的结构,例如复数。
回答 10
如果要根据参数类型调用不同的方法,请查看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
。
回答 11
简短的方法:
obj = 12345
print(isinstance(obj,int))
输出:
True
如果对象是字符串,则将返回’False’:
obj = 'some string'
print(isinstance(obj,int))
输出:
False
回答 12
您有一个数据项,说rec_day
当写入文件时将是一个float
。但程序处理期间,可以是float
,int
或str
类型(str
初始化一个新的记录时被使用并且包含一个伪标记的值)。
然后,您可以检查一下是否有此号码
type(rec_day) != str
我已经以这种方式构造了一个python程序,然后将其作为数字检查放入“维护补丁”中。这是Python方式吗?很可能没有,因为我以前使用COBOL编程。
回答 13
您可以使用isdigit()函数。
>>> x = "01234"
>>> a.isdigit()
True
>>> y = "1234abcd"
>>> y.isdigit()
False