如何在Python中检查字符串是否表示数值?

def is_number(s):
    try:
        float(s)
        return True
    except ValueError:
        return False

上述方法可行,但似乎很笨拙。


如果您正在测试的内容来自用户输入,那么即使它表示int或float,它仍然是一个字符串。请参阅如何将输入读取为数字?用于转换输入,并询问用户输入,直到他们给出有效响应以确保输入在继续之前表示int或float(或其他要求)。


当前回答

使用以下方法处理所有情况:-

import re
a=re.match('((\d+[\.]\d*$)|(\.)\d+$)' ,  '2.3') 
a=re.match('((\d+[\.]\d*$)|(\.)\d+$)' ,  '2.')
a=re.match('((\d+[\.]\d*$)|(\.)\d+$)' ,  '.3')
a=re.match('((\d+[\.]\d*$)|(\.)\d+$)' ,  '2.3sd')
a=re.match('((\d+[\.]\d*$)|(\.)\d+$)' ,  '2.3')

其他回答

TL;DR最佳解决方案是s.replace('.','',1).isdigit()

我做了一些比较不同方法的基准测试

def is_number_tryexcept(s):
    """ Returns True if string is a number. """
    try:
        float(s)
        return True
    except ValueError:
        return False
       
import re    
def is_number_regex(s):
    """ Returns True if 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 if string is a number. """
    return s.replace('.','',1).isdigit()

如果字符串不是数字,则except块非常慢。但更重要的是,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编号科学1='1.000000e+50'科学2=“1e50”print('不支持科学符号“1.0000000e+50”:')对于函数中的f:如果不是f(科学1):打印('\t-',f.name)print('不支持科学符号“1e50”:')对于函数中的f:如果不是f(科学2):打印('\t-',f.name)

以下各项不支持科学符号“1.0000000e+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 if string is a number. """
    try:
        float(s)
        return True
    except ValueError:
        return False

def is_number_regex(s):
    """ Returns True if 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 if string is a number. """
    if comp.match(s) is None:
        return s.isdigit()
    return True


def is_number_repl_isdigit(s):
    """ Returns True if string is a number. """
    return s.replace('.','',1).isdigit()

对于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

RyanN建议

若要为NaN和Inf返回False,请将行更改为x=float;返回(x==x)和(x-1!=x)。对于除Inf和NaN之外的所有浮点值,这都应返回True

但这并不完全有效,因为对于足够大的浮点数,x-1==x返回true。例如,2.0**54-1==2.0**54

我想看看哪种方法最快。总的来说,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

这不仅是丑陋和缓慢的,而且显得笨拙。

这可能需要一些时间来适应,但这是一种蟒蛇式的方式。正如已经指出的那样,替代方案更糟糕。但这样做还有一个好处:多态性。

duck类型背后的核心思想是“如果它像鸭子一样走路和说话,那么它就是鸭子。”如果您决定需要对字符串进行子类化,这样您就可以更改确定某个对象是否可以转换为float的方式,该怎么办?或者如果你决定完全测试其他对象呢?您可以在不必更改上述代码的情况下执行这些操作。

其他语言通过使用接口来解决这些问题。我将保存对哪个解决方案更适合另一个线程的分析。不过,重点是python显然是在公式中的鸭子类型方面,如果你打算用python进行大量编程,你可能必须习惯这样的语法(但这并不意味着你当然要喜欢它)。

还有一点您可能需要考虑:与许多其他语言相比,Python在抛出和捕获异常方面非常快(例如,比.Net快30倍)。见鬼,语言本身甚至抛出异常来传达非异常的正常程序条件(每次使用for循环时)。因此,在您注意到一个重大问题之前,我不会太担心这段代码的性能方面。