Answers:
使用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
当然,这与鸭子的打字相反。如果你更关心的对象如何行为,而不是它什么是,执行您的操作,如果你有一个号码,使用异常,否则告诉你。
isinstance(value, Number) and type(value) != bool
您要检查是否有物体
在某些情况下像数字一样
如果您使用的是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或更早,因此需要您自己进行检查)。
简要了解一下此类复杂性可能足以激励您在可行的情况下改为依赖抽象基类。
这是一个例外真正发光的好例子。只需执行对数字类型的处理,然后TypeError
从其他所有类型中捕获即可。
但是显然,这只会检查操作是否有效,而不是是否有意义!唯一真正的解决方案是永远不要混合类型,并且始终确切地知道您的值属于什么类型类。
isinstance
其实是可以有用在许多情况下(==“检查是有意义的”,以及正式的适用性操作)。对于长期使用Python的人们来说,这是艰难的转变,但是在Python哲学中一个非常重要的微妙趋势是,忽略它将是一个严重的错误。
collections.Sequence
朋友)。但是afaik,没有针对数字,向量或任何其他数学对象的此类类。
将对象乘以零。任何数字乘以零就是零。其他任何结果均表示该对象不是数字(包括异常)
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])
True * 0 == 0
int
因此我的函数将正确地说布尔值是数字。
为了改写您的问题,您正在尝试确定某物是集合还是单个值。试图比较某物是矢量还是数字,就是将苹果与橘子进行比较-我可以使用字符串或数字的矢量,也可以使用单个字符串或数字。您对拥有的数量(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]
最终,测试某事物是否类似于矢量比测试某事物是否类似于标量要容易。如果您遇到不同类型的值(例如,字符串,数字等),那么程序的逻辑可能需要做些工作-您最终是如何尝试将字符串乘以数字向量的?
总结/评估现有方法:
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
对于假设的向量类:
假设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]
将产生的外积x
的v
。这是2级张量,不是标量。
comp*x
将规模x
由comp
,我是假设,这将引发TypeError。不幸的是,它实际上会x
与comp
时间联系在一起。哎呀。
x
是向量,则它应具有__rmul__
方法(__rmul__ = __mul__
),以便comp * x
应按明显打算的x
方式进行 缩放x * comp
。
如果要根据参数类型调用不同的方法,请查看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
。