检查字符串是否可以在Python中表示为数字的最佳方法是什么?
我目前拥有的功能是:
def is_number(s): try: float(s) return True except ValueError: return False
这不仅是丑陋而且缓慢,似乎很笨重.但是我没有找到更好的方法,因为调用float
main函数更糟糕.
如果您正在寻找解析(正,无符号)整数而不是浮点数,则可以将该isdigit()
函数用于字符串对象.
>>> a = "03523" >>> a.isdigit() True >>> b = "963spam" >>> b.isdigit() False
字符串方法 - isdigit()
在Unicode字符串上也有一些东西,我对Unicode不太熟悉 - 是十进制/十进制
其中,不仅是丑陋而且缓慢
我两个都有争议.
正则表达式或其他字符串解析将更加丑陋和缓慢.
我不确定任何事情都可能比上面更快.它调用函数并返回.Try/Catch不会引入太多开销,因为最常见的异常是在没有大量搜索堆栈帧的情况下捕获的.
问题是任何数字转换函数都有两种结果
如果数字有效,则为数字
状态代码(例如,通过errno)或异常以显示无法解析有效数字.
C(作为一个例子)以多种方式解决这个问题.Python明确而明确地阐述了它.
我认为你这样做的代码是完美的.
TL; DR最好的解决方案是s.replace('.','',1).isdigit()
我做了一些比较不同方法的基准
def is_number_tryexcept(s): """ Returns True is string is a number. """ try: float(s) return True except ValueError: return False import re def is_number_regex(s): """ Returns True is string is a number. """ if re.match("^\d+?\.\d+?$", s) is None: return s.isdigit() return True def is_number_repl_isdigit(s): """ Returns True is string is a number. """ return s.replace('.','',1).isdigit()
如果字符串不是数字,则except-block非常慢.但更重要的是,try-except方法是正确处理科学记数法的唯一方法.
funcs = [ is_number_tryexcept, is_number_regex, is_number_repl_isdigit ] a_float = '.1234' print('Float notation ".1234" is not supported by:') for f in funcs: if not f(a_float): print('\t -', f.__name__)
以下不支持浮点表示法".1234":
- is_number_regex
scientific1 = '1.000000e+50' scientific2 = '1e50' print('Scientific notation "1.000000e+50" is not supported by:') for f in funcs: if not f(scientific1): print('\t -', f.__name__) print('Scientific notation "1e50" is not supported by:') for f in funcs: if not f(scientific2): print('\t -', f.__name__)
不支持科学计数法"1.000000e + 50":
- is_number_regex
- is_number_repl_isdigit
不支持科学计数法"1e50":
- is_number_regex
- is_number_repl_isdigit
import timeit test_cases = ['1.12345', '1.12.345', 'abc12345', '12345'] times_n = {f.__name__:[] for f in funcs} for t in test_cases: for f in funcs: f = f.__name__ times_n[f].append(min(timeit.Timer('%s(t)' %f, 'from __main__ import %s, t' %f) .repeat(repeat=3, number=1000000)))
测试以下功能的地方
from re import match as re_match from re import compile as re_compile def is_number_tryexcept(s): """ Returns True is string is a number. """ try: float(s) return True except ValueError: return False def is_number_regex(s): """ Returns True is string is a number. """ if re_match("^\d+?\.\d+?$", s) is None: return s.isdigit() return True comp = re_compile("^\d+?\.\d+?$") def compiled_regex(s): """ Returns True is string is a number. """ if comp.match(s) is None: return s.isdigit() return True def is_number_repl_isdigit(s): """ Returns True is string is a number. """ return s.replace('.','',1).isdigit()
您可能需要考虑一个例外:字符串'NaN'
如果你想让is_number为'NaN'返回FALSE,那么这段代码将不起作用,因为Python将它转换为一个不是数字的数字的表示(谈论身份问题):
>>> float('NaN') nan
否则,我实际上应该感谢你现在广泛使用的代码片段.:)
G.
这个怎么样:
'3.14'.replace('.','',1).isdigit()
只有在有'或'的情况下才会返回true.在数字串中.
'3.14.5'.replace('.','',1).isdigit()
将返回false
编辑:刚刚看到另一条评论... .replace(badstuff,'',maxnum_badstuff)
可以为其他案例添加一个.如果你传递盐而不是任意调味品(参考:xkcd#974),这样做会很好:P
Alfe指出您不需要单独检查浮动,因为复杂处理两者:
def is_number(s): try: complex(s) # for int, long, float and complex except ValueError: return False return True
之前说过:在一些罕见的情况下,您可能还需要检查复数(例如1 + 2i),这不能用浮点数表示:
def is_number(s): try: float(s) # for int, long and float except ValueError: try: complex(s) # for complex except ValueError: return False return True
这不仅是丑陋而且缓慢,似乎很笨重.
这可能需要一些时间来习惯,但这是做到这一点的pythonic方式.正如已经指出的那样,替代方案更糟糕.但是以这种方式做事还有另一个好处:多态性.
鸭子打字背后的核心理念是"如果它像鸭子一样走路和说话,那么它就是一只鸭子." 如果您决定需要子类化字符串以便可以更改确定是否可以将某些内容转换为浮点数的方式,该怎么办?或者如果您决定完全测试其他对象怎么办?您无需更改上述代码即可完成这些操作.
其他语言通过使用接口解决了这些问题.我将保存分析哪个解决方案更适合另一个线程.但问题是,python肯定是在等式的鸭子打字方面,如果你打算用Python做很多编程,你可能不得不习惯这样的语法(但这并不意味着你必须喜欢它当然).
您可能需要考虑的另一件事是:与许多其他语言相比,Python在抛出和捕获异常方面相当快(例如,比.Net快30倍).哎呀,语言本身甚至会抛出异常来传达非常规的正常程序条件(每次使用for循环).因此,在您发现重大问题之前,我不会过多担心此代码的性能方面.
用于int
此:
>>> "1221323".isdigit() True
但是因为float
我们需要一些技巧;-).每个浮点数都有一点......
>>> "12.34".isdigit() False >>> "12.34".replace('.','',1).isdigit() True >>> "12.3.4".replace('.','',1).isdigit() False
对于负数,只需添加lstrip()
:
>>> '-12'.lstrip('-') '12'
现在我们得到一个通用的方式:
>>> '-12.34'.lstrip('-').replace('.','',1).isdigit() True >>> '.-234'.lstrip('-').replace('.','',1).isdigit() False
在C#中,有两个不同的函数来处理标量值的解析:
Float.Parse()
Float.TryParse()
float.parse():
def parse(string): try: return float(string) except Exception: throw TypeError
注意:如果您想知道为什么我将异常更改为TypeError,请参阅文档.
float.try_parse():
def try_parse(string, fail=None): try: return float(string) except Exception: return fail;
注意:您不希望返回布尔值'False',因为它仍然是值类型.没有比这更好,因为它表明失败.当然,如果您想要不同的东西,可以将fail参数更改为您想要的任何内容.
要扩展float以包含'parse()'和'try_parse()',您需要monkeypatch'float'类来添加这些方法.
如果您想要尊重预先存在的函数,代码应该是这样的:
def monkey_patch(): if(!hasattr(float, 'parse')): float.parse = parse if(!hasattr(float, 'try_parse')): float.try_parse = try_parse
SideNote:我个人更喜欢称之为Monkey Punching,因为当我这样做时,感觉就像是在滥用语言而是YMMV.
用法:
float.parse('giggity') // throws TypeException float.parse('54.3') // returns the scalar value 54.3 float.tryParse('twank') // returns None float.tryParse('32.2') // returns the scalar value 32.2
伟大的Sage Pythonas向教廷Sharpisus说:"你能做的任何事情我都可以做得更好;我能做比你更好的事情."
对于非数字字符串,try: except:
实际上比正则表达式慢.对于有效数字的字符串,正则表达式较慢.因此,适当的方法取决于您的输入.
如果您发现自己处于性能绑定状态,则可以使用名为fastnumbers的新第三方模块,该模块提供名为isfloat的函数.完全披露,我是作者.我已将结果包含在下面的时间中.
from __future__ import print_function import timeit prep_base = '''\ x = 'invalid' y = '5402' z = '4.754e3' ''' prep_try_method = '''\ def is_number_try(val): try: float(val) return True except ValueError: return False ''' prep_re_method = '''\ import re float_match = re.compile(r'[-+]?\d*\.?\d+(?:[eE][-+]?\d+)?$').match def is_number_re(val): return bool(float_match(val)) ''' fn_method = '''\ from fastnumbers import isfloat ''' print('Try with non-number strings', timeit.timeit('is_number_try(x)', prep_base + prep_try_method), 'seconds') print('Try with integer strings', timeit.timeit('is_number_try(y)', prep_base + prep_try_method), 'seconds') print('Try with float strings', timeit.timeit('is_number_try(z)', prep_base + prep_try_method), 'seconds') print() print('Regex with non-number strings', timeit.timeit('is_number_re(x)', prep_base + prep_re_method), 'seconds') print('Regex with integer strings', timeit.timeit('is_number_re(y)', prep_base + prep_re_method), 'seconds') print('Regex with float strings', timeit.timeit('is_number_re(z)', prep_base + prep_re_method), 'seconds') print() print('fastnumbers with non-number strings', timeit.timeit('isfloat(x)', prep_base + 'from fastnumbers import isfloat'), 'seconds') print('fastnumbers with integer strings', timeit.timeit('isfloat(y)', prep_base + 'from fastnumbers import isfloat'), 'seconds') print('fastnumbers with float strings', timeit.timeit('isfloat(z)', prep_base + 'from fastnumbers import isfloat'), 'seconds') print()
Try with non-number strings 2.39108395576 seconds Try with integer strings 0.375686168671 seconds Try with float strings 0.369210958481 seconds Regex with non-number strings 0.748660802841 seconds Regex with integer strings 1.02021503448 seconds Regex with float strings 1.08564686775 seconds fastnumbers with non-number strings 0.174362897873 seconds fastnumbers with integer strings 0.179651021957 seconds fastnumbers with float strings 0.20222902298 seconds
如你看到的
try: except:
数字输入速度很快,但输入无效则很慢
当输入无效时,正则表达式非常有效
fastnumbers
两种情况都胜出
我知道这个特别古老,但我会添加一个答案,我认为这些答案涵盖了最高投票答案中缺少的信息,对于任何发现这一点的人来说都是非常有价值的:
对于以下每种方法,如果需要接受任何输入,请将它们与计数相连.(假设我们使用整数的声音定义而不是0-255等)
x.isdigit()
适用于检查x是否为整数.
x.replace('-','').isdigit()
适用于检查x是否为负数.(签到第一个位置)
x.replace('.','').isdigit()
适用于检查x是否为小数.
x.replace(':','').isdigit()
适用于检查x是否为比率.
x.replace('/','',1).isdigit()
适用于检查x是否为分数.
转换为float并捕获ValueError可能是最快的方法,因为float()专门用于此.任何需要字符串解析(正则表达式等)的东西都可能会因为没有针对此操作进行调整而变慢.我的0.02美元.
你可以使用Unicode字符串,他们有一个方法可以做你想要的:
>>> s = u"345" >>> s.isnumeric() True
要么:
>>> s = "345" >>> u = unicode(s) >>> u.isnumeric() True
http://www.tutorialspoint.com/python/string_isnumeric.htm
http://docs.python.org/2/howto/unicode.html
这个答案提供了一步一步的指南,其中有一些函数用例来查找字符串:
正整数
正/负 - 整数/浮点数
如何在检查号码时丢弃"NaN"(不是数字)字符串?
检查字符串是否为正整数
您可以使用str.isdigit()
检查给定字符串是否为正整数.
样本结果:
# For digit >>> '1'.isdigit() True >>> '1'.isalpha() False检查字符串为正/负 - 整数/浮点数
str.isdigit()
False
如果字符串是负数或浮点数,则返回.例如:
# returns `False` for float >>> '123.3'.isdigit() False # returns `False` for negative number >>> '-123'.isdigit() False
如果你还想检查负整数float
,那么你可以编写一个自定义函数来检查它:
def is_number(n): try: float(n) # Type-casting the string to `float`. # If string is not a valid `float`, # it'll raise `ValueError` exception except ValueError: return False return True
样品运行:
>>> is_number('123') # positive integer number True >>> is_number('123.4') # positive float number True >>> is_number('-123') # negative integer number True >>> is_number('-123.4') # negative `float` number True >>> is_number('abc') # `False` for "some random" string False检查号码时丢弃"NaN"(不是数字)字符串
上面的函数将返回True
"NAN"(非数字)字符串,因为对于Python,它是有效的float,表示它不是数字.例如:
>>> is_number('NaN') True
为了检查数字是否为"NaN",您可以使用math.isnan()
:
>>> import math >>> nan_num = float('nan') >>> math.isnan(nan_num) True
或者,如果您不想导入其他库来检查这一点,那么您可以通过比较它自己来检查它==
.False
当nan
float与自身进行比较时,Python会返回.例如:
# `nan_num` variable is taken from above example >>> nan_num == nan_num False
因此,上述功能is_number
可以更新,返回False
的"NaN"
是:
def is_number(n): is_number = True try: num = float(n) # check for "nan" floats is_number = num == num # or use `math.isnan(num)` except ValueError: is_number = False return is_number
样品运行:
>>> is_number('Nan') # not a number "Nan" string False >>> is_number('nan') # not a number string "nan" with all lower cased False >>> is_number('123') # positive integer True >>> is_number('-123') # negative integer True >>> is_number('-1.12') # negative `float` True >>> is_number('abc') # "some random" string False
PS:每个检查的每个操作取决于数字的类型带来额外的开销.选择is_number
适合您要求的功能版本.
我想看看哪种方法最快.总体而言,该check_replace
功能给出了最佳和最一致的结果.最快的结果是由check_exception
函数给出的,但是只有在没有异常被触发的情况下 - 意味着它的代码是最有效的,但抛出异常的开销非常大.
请注意,检查成功的强制转换是唯一准确的方法,例如,这可以使用,check_exception
但其他两个测试函数将为有效的float返回False:
huge_number = float('1e+100')
这是基准代码:
import time, re, random, string ITERATIONS = 10000000 class Timer: def __enter__(self): self.start = time.clock() return self def __exit__(self, *args): self.end = time.clock() self.interval = self.end - self.start def check_regexp(x): return re.compile("^\d*\.?\d*$").match(x) is not None def check_replace(x): return x.replace('.','',1).isdigit() def check_exception(s): try: float(s) return True except ValueError: return False to_check = [check_regexp, check_replace, check_exception] print('preparing data...') good_numbers = [ str(random.random() / random.random()) for x in range(ITERATIONS)] bad_numbers = ['.' + x for x in good_numbers] strings = [ ''.join(random.choice(string.ascii_uppercase + string.digits) for _ in range(random.randint(1,10))) for x in range(ITERATIONS)] print('running test...') for func in to_check: with Timer() as t: for x in good_numbers: res = func(x) print('%s with good floats: %s' % (func.__name__, t.interval)) with Timer() as t: for x in bad_numbers: res = func(x) print('%s with bad floats: %s' % (func.__name__, t.interval)) with Timer() as t: for x in strings: res = func(x) print('%s with strings: %s' % (func.__name__, t.interval))
以下是2017年MacBook Pro 13上Python 2.7.10的结果:
check_regexp with good floats: 12.688639 check_regexp with bad floats: 11.624862 check_regexp with strings: 11.349414 check_replace with good floats: 4.419841 check_replace with bad floats: 4.294909 check_replace with strings: 4.086358 check_exception with good floats: 3.276668 check_exception with bad floats: 13.843092 check_exception with strings: 15.786169
以下是2017年MacBook Pro 13上Python 3.6.5的结果:
check_regexp with good floats: 13.472906000000009 check_regexp with bad floats: 12.977665000000016 check_regexp with strings: 12.417542999999995 check_replace with good floats: 6.011045999999993 check_replace with bad floats: 4.849356 check_replace with strings: 4.282754000000011 check_exception with good floats: 6.039081999999979 check_exception with bad floats: 9.322753000000006 check_exception with strings: 9.952595000000002
以下是2017款MacBook Pro 13上PyPy 2.7.13的结果:
check_regexp with good floats: 2.693217 check_regexp with bad floats: 2.744819 check_regexp with strings: 2.532414 check_replace with good floats: 0.604367 check_replace with bad floats: 0.538169 check_replace with strings: 0.598664 check_exception with good floats: 1.944103 check_exception with bad floats: 2.449182 check_exception with strings: 2.200056
所以把它们放在一起,检查Nan,无穷大和复数(看起来它们用j指定,而不是i,即1 + 2j)它导致:
def is_number(s): try: n=str(float(s)) if n == "nan" or n=="inf" or n=="-inf" : return False except ValueError: try: complex(s) # for complex except ValueError: return False return True
输入可能如下:
a="50"
b=50
c=50.1
d="50.1"
此功能的输入可以是所有内容!
查找给定变量是否为数字。数字字符串包括可选符号,任意数量的数字,可选小数部分和可选指数部分。因此,+ 0123.45e6是有效的数值。不允许使用十六进制(例如0xf4c3b00c)和二进制(例如0b10100111001)表示法。
is_numeric函数
import ast import numbers def is_numeric(obj): if isinstance(obj, numbers.Number): return True elif isinstance(obj, str): nodes = list(ast.walk(ast.parse(obj)))[1:] if not isinstance(nodes[0], ast.Expr): return False if not isinstance(nodes[-1], ast.Num): return False nodes = nodes[1:-1] for i in range(len(nodes)): #if used + or - in digit : if i % 2 == 0: if not isinstance(nodes[i], ast.UnaryOp): return False else: if not isinstance(nodes[i], (ast.USub, ast.UAdd)): return False return True else: return False
测试:
>>> is_numeric("54") True >>> is_numeric("54.545") True >>> is_numeric("0x45") True
is_float函数
查找给定变量是否为float。浮点字符串包含可选符号,任意数量的数字,...
import ast def is_float(obj): if isinstance(obj, float): return True if isinstance(obj, int): return False elif isinstance(obj, str): nodes = list(ast.walk(ast.parse(obj)))[1:] if not isinstance(nodes[0], ast.Expr): return False if not isinstance(nodes[-1], ast.Num): return False if not isinstance(nodes[-1].n, float): return False nodes = nodes[1:-1] for i in range(len(nodes)): if i % 2 == 0: if not isinstance(nodes[i], ast.UnaryOp): return False else: if not isinstance(nodes[i], (ast.USub, ast.UAdd)): return False return True else: return False
测试:
>>> is_float("5.4") True >>> is_float("5") False >>> is_float(5) False >>> is_float("5") False >>> is_float("+5.4") True
什么是ast?
使用str.isdigit()方法
>>> a=454 >>> a.isdigit() Traceback (most recent call last): File "", line 1, in AttributeError: 'int' object has no attribute 'isdigit' >>> a="454" >>> a.isdigit() True
检测int值:
>>> isinstance("54", int) False >>> isinstance(54, int) True >>>
检测浮动:
>>> isinstance("45.1", float) False >>> isinstance(45.1, float) True