繁体   English   中英

Python:验证字符串是否为未转换的浮点型

[英]Python: validate whether string is a float without conversion

有没有一种Python的方法来验证字符串是否表示浮点数(任何可以由float()识别的输入,例如-1.6e3 ),而无需对其进行转换(并且,理想的是,不求助于抛出和捕获异常) ?

关于如何检查字符串是整数还是浮点数,以前的问题已经提交。 答案建议在用户定义的函数中使用try...except子句以及int()float()内置函数。

但是,这些都没有正确解决速度问题。 当使用try...except惯用语将转换过程与验证过程联系在一起(在某种程度上是正确的)时,为验证目的而使用大量文本的应用程序(任何模式验证器,解析器)将遭受开销执行实际转换。 除了由于实际转换数字导致的速度下降外,还有引发和捕获异常导致的速度下降。 该GitHub要点演示了,与仅用户定义的验证相比,内置转换代码的成本(比较True案例)要高两倍,而异常处理时间( False time减去try..except版本的True time)仅是如下:多达7个验证。 这回答了我关于整数的问题。

有效的答案将是:与try..except方法相比,以更有效的方式解决问题的函数;对将来将允许使用此功能的内置功能的文档的引用;对允许此功能的Python包的引用现在(并且比try..except方法更有效),或者解释说明为什么这样的解决方案不是Pythonic的,否则将永远不会实现。 具体说来,为避免混乱,请避免使用诸如“否”之类的答案,而无需指出官方文档或邮件列表辩论,并避免重复try..except方法。

正如@John在评论中提到的那样,这似乎是另一个问题答案 ,尽管在这种情况下这不是公认的答案。 正则表达式和fastnumbers模块是此问题的两种解决方案。

但是,应该适当地指出(如@en_Knight所做的那样),性能很大程度上取决于输入。 如果期望得到大多数有效输入,那么EAFP方法会更快,并且可以说更优雅。 如果您不知道要输入什么,那么LBYL可能更合适。 本质上,验证应该期望大多数有效输入,因此它更适合try..except

事实是,对于我的用例(以及与它相关的问题的作者)来说,确定表格数据文件中的数据类型, try..except方法更合适:一列要么全部为浮点数,要么,如果它具有非浮点值,则从该行开始将其视为文本,因此,实际测试的大多数输入在两种情况下均有效。 我猜所有其他答案都在解决。

回到答案,快速数和正则表达式仍然是一般情况下的吸引人的解决方案。 具体来说,fastnumbers包似乎对所有值都适用,除了特殊值(例如InfinityInfNaN ,如本GitHub要点所示 对于上述答案中的简单正则表达式也是如此(稍作修改-删除了结尾的\\b ,因为它将导致某些输入失败):

^[-+]?(?:\b[0-9]+(?:\.[0-9]*)?|\.[0-9]+)(?:[eE][-+]?[0-9]+\b)?$

要点使用了体积较大的版本,可以识别特殊值,并且具有相同的性能:

^[-+]?(?:[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倍,而使用正则表达式的输入try..except慢1.3倍。 鉴于这些结果,这意味着在40%或更多的预期输入无效时使用正则表达式是有利的。

在有效输入上,fastnumbers仅快约1.2倍,但是在无效输入上,fastnumbers快约6.3倍。

结果在下图中描述。 我用10 ^ 6次重复运行,有170个有效输入和350个无效输入(经过相应加权,因此平均时间是每个单个输入)。 由于框太窄,所以未显示颜色,但每列左侧的框描述了有效输入的时间,而无效输入在右侧。

根据输入是否有效来验证字符串是否持有有效浮点值的方法的时间安排

注意答案已被多次编辑以反映对问题,此答案和其他答案的评论。 为了清楚起见,已合并了编辑内容。 一些评论是指以前的版本。

如果使用pythonic是合理的,那么您应该坚持使用Python之禅 专门针对这些:

显式胜于隐式。

简单胜于复杂。

可读性很重要。

应该有一种-最好只有一种-显而易见的方法。

如果实现难以解释,那是个坏主意。

所有这些都支持“尝试除外”方法。 转换是显式的,简单的,可读的,显而易见的且易于解释的

另外,知道某物是否为浮点数的唯一方法是测试它是否为浮点数。 这听起来可能是多余的,但并非如此

现在,如果主要问题是尝试测试过多的浮点数时的速度,则可以使用带有cython的C扩展名一次测试所有这些扩展。 但是我真的不认为它会在速度方面带来太多改进,除非要尝试的字符串数量很大

编辑:

Python开发人员倾向于使用EAFP方法(比许可更容易请求宽恕),从而使try-except方法更具pythonic能力(我找不到PEP)

这里( Python中异常处理程序的代价 )是try-except方法与if-then方法之间的比较。 事实证明,在Python中,异常处理并不像在其他语言中那样昂贵,并且仅在必须处理异常的情况下才更昂贵。 而且在一般使用情况下,您将不会尝试以很高的概率来验证字符串,而该字符串实际上不是浮点数(除非在您的特定情况下,您会遇到这种情况)。

再次如我在评论中所说。 没有特定的用例,要测试的数据和时间的度量,整个问题就没有多大意义了。 只是在谈论最通用的用例时,try-except是可行的方法,如果您有一些实际需求无法快速满足,则应将其添加到问题中

为了证明这一点:字符串可以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要慢很多try: float(string); return True; except Exception: return False try: float(string); return True; except Exception: return False

如果您确定要快速解决方案

看一下这个参考实现 -python中的float转换是在C代码中进行的,并且执行效率很高。 如果您真的担心开销,可以将代码原样复制到自定义的C扩展中,但是不要抛出错误标志,而是返回一个表示成功的布尔值。

特别要看一下实现将十六进制强制转换为浮点数的复杂逻辑。 这是在C级别上完成的,有很多错误情况。 似乎不太可能在这里有捷径(请注意针对一个特定的保护案例争论的40行注释),或者在保留这些案例的情况下,任何手动实施的实现都会更快。

但是...有必要吗?

作为一个假设,这个问题很有趣,但是在一般情况下,应该尝试分析其代码,以确保try catch方法会增加开销。 尝试/捕获通常是惯用的 ,而且根据您的使用情况可以更快 例如,python中的for循环是按设计使用try / catch的。

替代方案以及为什么我不喜欢它们

为了澄清,问题询问

float()可以识别的任何输入

选择#1-正则表达式如何

我很难相信您会获得一个正则表达式来解决这个问题。 尽管正则表达式可以很好地捕获浮点文字,但仍有很多极端情况。 查看此答案的所有情况-您的正则表达式可以处理NaN吗? 指数? 布尔(但不是布尔字符串)?

方案2:手动解开Python检查:

总结需要捕获的困难情况(Python本身就是这样做的)

我还要指出的是语言规范中浮点以下的情况; 虚数。 浮动方法通过识别它们是什么来优雅地处理它们,但是在转换时引发类型错误。 您的自定义方法会模仿这种行为吗?

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM