是否有Pythonic方法来验证字符串是否表示浮点数(float()
可以识别的任何输入,例如-1.6e3
),而不是转换它(并且理想情况下,没有诉诸投掷和捕捉例外)?
之前的问题已经提交,说明如何检查字符串是代表integer还是float。答案建议在用户定义的函数中将try...except
子句与int()
和float()
内置函数一起使用。
然而,这些并没有妥善解决速度问题。虽然使用try...except
惯用法将转换过程与验证过程联系起来(在某种程度上是正确的),但是为了验证目的而重复大量文本的应用程序(任何模式验证器,解析器)将受到开销的影响执行实际转换。除了由于数字的实际转换而导致的减速之外,还存在因抛出和捕获异常而导致的减速。 This GitHub gist演示了与仅用户定义的验证相比,内置转换代码的成本是两倍(比较True
个案例)和异常处理时间(False
时间减去{{1单独True
版本的时间)就是7次验证。对于整数的情况,这回答了我的问题。
有效的答案将是:以比try..except
方法更有效的方式解决问题的函数,对未来允许的内置特性的文档的引用,对Python的引用现在允许这个的包(并且比try..except
方法更有效),或者说明为什么这样的解决方案不是Pythonic,或者否则将永远不会被实现的文档的解释。具体来说,为了防止混乱,请避免使用“否”等答案而不指向官方文档或邮件列表辩论,并避免重复try..except
方法。
答案 0 :(得分:4)
正如@John在评论中提到的那样,这显示为answer in another question,尽管在这种情况下它不是公认的答案。正则表达式和fastnumbers模块是解决此问题的两种方法。
但是,它正确地指出(正如@en_Knight所做的那样)表现在很大程度上取决于投入。如果期望大多数是有效的输入,那么EAFP方法更快,可以说更优雅。如果您不知道要输入什么,那么LBYL可能更合适。从本质上讲,验证应该主要是有效的输入,因此它更适合try..except
。
事实上,对于我在表格数据文件中识别数据类型的用例(以及作为相关问题的作者),try..except
方法更合适:列是全部浮点数,或者,如果它具有非浮点值,则从它上面的那一行开始考虑文本,因此实际测试浮点数的大多数输入在任何一种情况下都是有效的。我猜所有其他答案都是针对某些事情的。
回到答案,快速数字和正则表达式仍然是一般案例的吸引人的解决方案。具体来说,fastnumbers包似乎适用于除Infinity
,Inf
和NaN
等特殊值以外的所有值,如this GitHub gist中所示。上述答案中的简单正则表达式也是如此(略微修改 - 删除尾随\b
,因为它会导致某些输入失败):
^[-+]?(?:\b[0-9]+(?:\.[0-9]*)?|\.[0-9]+)(?:[eE][-+]?[0-9]+\b)?$
在gist中使用了确实识别特殊值的笨重版本,并具有相同的性能:
^[-+]?(?:[Nn][Aa][Nn]|[Ii][Nn][Ff](?:[Ii][Nn][Ii][Tt][Yy])?|(?:\b[0-9]+(?:\.[0-9]*)?|\.[0-9]+)(?:[eE][-+]?[0-9]+\b)?)$
正则表达式实现在有效输入上慢约2.8倍,但在无效输入上快〜2.2倍。无效输入比使用try..except
的有效输入慢〜5倍,或使用正则表达式快〜1.3倍。鉴于这些结果,当40%或更多的预期输入无效时,这意味着使用正则表达式是有利的。
快速数字在有效输入上仅快约1.2倍,但在无效输入上快约6.3倍。
结果如下图所示。我跑了10 ^ 6次重复,有170个有效输入和350个无效输入(相应地加权,所以平均时间是每个输入)。颜色不显示,因为框太窄,但每列左侧的颜色描述有效输入的时间,而无效输入则在右侧。
注意答案被多次编辑,以反映对问题,答案和其他答案的评论。为清楚起见,编辑已合并。一些评论涉及以前的版本。
答案 1 :(得分:0)
如果是pythonic是理由,那么你应该坚持The Zen of Python。特别针对这些:
明确比隐含更好。
简单比复杂更好。
可读性计数。
应该有一个 - 最好只有一个 - 显而易见的方法。
如果实施难以解释,那就不好了。
所有这些人都支持try-except方法。转换是明确的,简单的,可读的,明显的且易于解释
此外,知道某些东西是否为浮点数的唯一方法是测试它是否为浮点数。这可能听起来多余,但它不是
现在,如果主要问题是在尝试测试过多的假设浮点数时的速度,你可以使用一些带有cython的C扩展来一次测试所有这些。但我并不认为它会在速度方面给你太多的改进,除非要尝试的字符串数量非常大
编辑:
Python开发人员倾向于选择EAFP方法(更容易要求宽恕而不是权限),使得try-except方法更加pythonic(我无法找到PEP)
这里(Cost of exception handlers in Python)是针对if-then的try-except方法之间的比较。事实证明,在Python中,异常处理并不像在其他语言中那样昂贵,并且在必须处理异常的情况下,它的成本更高。在一般用例中,您不会尝试验证字符串很可能实际上不是浮点数(除非在您的特定情况下,您有这种情况)。
正如我在评论中所说的那样。如果没有特定的用例,要测试的数据和时间的测量,整个问题就没有多大意义。只是谈论最通用的用例,尝试 - 除了是要走的路,如果你有一些实际需要不能足够快地满足它,那么你应该把它添加到问题
答案 2 :(得分:-1)
要证明一点:字符串必须遵守的条件并不多,才能float
能够。但是,在Python中检查所有这些条件会相当慢。
ALLOWED = "0123456789+-eE."
def is_float(string):
minuses = string.count("-")
if minuses == 1 and string[0] != "-":
return False
if minuses > 1:
return False
pluses = string.count("+")
if pluses == 1 and string[0] != "+":
return False
if pluses > 1:
return False
points = string.count(".")
if points > 1:
return False
small_es = string.count("e")
large_es = string.count("E")
es = small_es + large_es
if es > 1:
return False
if (es == 1) and (points == 1):
if small_es == 1:
if string.index(".") > string.index("e"):
return False
else:
if string.index(".") > string.index("E"):
return False
return all(char in ALLOWED for char in string)
我实际上没有对此进行测试,但我愿意打赌这比try: float(string); return True; except Exception: return False
答案 3 :(得分:-1)
快速解决方案,如果你确定你想要它
看一下this reference implementation - 在python中转换为float会在C代码中发生,并且执行效率非常高。如果您真的担心开销,可以将该代码逐字复制到自定义C扩展中,但不是引发错误标志,而是返回表示成功的布尔值。
特别是,请查看为将十六进制强制转换为float而实现的复杂逻辑。这是在C级完成的,有很多错误情况;这里似乎不太可能有一条捷径(请注意40条评论争论一个特定的保护案例),或者在保留这些案例时任何手动实施都会更快。
但是......有必要吗?
作为一个假设,这个问题很有意思,但在一般情况下,应该尝试对其代码进行分析,以确保try catch方法增加了开销。根据您的使用情况,尝试/捕捉is often idiomatic以及can be faster。例如,python中的for循环使用try / catch by design。
替代方案以及为什么我不喜欢它们
澄清一下,问题是关于
float()
可识别的任何输入
备选方案#1 - 正则表达式
我发现很难相信你会得到一个正则表达式来解决这个问题。虽然正则表达式擅长捕获浮点文字,但有很多极端情况。查看this answer上的所有案例 - 您的正则表达式是否处理NaN
?指数? Bool(但不是bool弦)?
备选方案#2:手动取消版本的Python检查:
总结需要捕获的棘手案例(Python本身就是这样做的)
我还会指出语言规范中浮点以下的情况;想象中的数字。浮动方法通过识别它们的优点来处理这些,但在转换时抛出类型错误。您的自定义方法会模仿该行为吗?