What is the most pythonic way to check if an object is a number?
对于任意的python对象,确定它是否是数字的最佳方法是什么?这里,
例如,假设您正在编写向量类。如果给另一个向量,你想找到点积。如果给定一个标量,则需要缩放整个向量。
检查是否有东西是
使用来自
1 2 3 4 5 6 7 8 9 10 11 | >>> 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 |
当然,这与鸭子打字相反。如果您更关心一个对象是如何工作的,而不是它是什么,请像有数字一样执行操作,并使用异常来告诉您其他情况。
你想检查一下
acts like a number in certain
circumstances
如果您使用的是Python2.5或更高版本,唯一真正的方法是检查其中的一些"特定情况",然后查看。
在2.6或更高版本中,您可以使用带数字.number的
巴赫到2.5及更早,在某些情况下,"可以添加到
在2.6或更高版本中也可能需要这样做,可能是为了让您自己注册以添加您所关心的尚未注册到
顺便说一句,如果您需要检查
1 2 3 | try: 0 + x except TypeError: canadd=False else: canadd=True |
对这种复杂情况的简要了解可能足以激励您在可行的情况下依赖抽象基类……;-)。
这是一个很好的例子,在这个例子中,异常情况确实很突出。只需对数字类型做您想做的,并从其他所有类型捕获
但很明显,这只检查操作是否有效,而不是是否有意义!唯一真正的解决方案是永远不要混合类型,并且总是确切地知道值属于什么类型类。
把物体乘以零。任何乘以零的数字都是零。任何其他结果表示对象不是数字(包括异常)
1 2 3 4 5 | def isNumber(x): try: return 0 == x*0 except: return False |
使用isNumber会得到以下输出:
1 2 3 4 5 6 7 | 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()) |
输出:
1 2 3 4 5 6 | 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) |
世界上可能有一些非数字对象将
为了重新表述您的问题,您试图确定某个东西是集合还是单个值。试图比较某个东西是向量还是数字,这是在比较苹果和橙子——我可以有一个字符串或数字的向量,我可以有一个字符串或单个数字。你感兴趣的是你有多少(1个或更多),而不是你实际拥有的类型。
对于这个问题,我的解决方案是通过检查
1 2 3 4 5 | 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类型方法,您可以尝试首先在
1 2 3 4 5 | 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] |
归根结底,测试某个东西是否像向量,比测试某个东西是否像标量要容易得多。如果您有不同类型的值(如字符串、数字等),那么您的程序逻辑可能需要一些工作-您是如何在第一时间尝试将字符串乘以数字向量的?
总结/评估现有方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | 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 |
(我是通过这个问题来到这里的)
代码1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 | #!/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():
如果要根据参数类型调用不同的方法,请查看
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.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | 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] |
不幸的是,(据我所知)我们不能编写
在实现一类向量类时,我遇到了类似的问题。检查数字的一种方法是将其转换为一,即使用
1 | float(x) |
这应该会拒绝x不能转换为数字的情况;但也可能会拒绝其他类型的数字,例如可能有效的结构,例如复数。
对于假设向量类:
假设
编辑——下面的代码不起作用,因为
1 2 3 4 5 | 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 ) |
您可以使用isdigit()函数。
1 2 3 4 5 6 | >>> x ="01234" >>> a.isdigit() True >>> y ="1234abcd" >>> y.isdigit() False |