числа - проверка является ли строка числом python




Как проверить, является ли строка числом(float)? (20)

Каков наилучший способ проверить, может ли строка быть представлена ​​в виде числа в Python?

Функция, которую я сейчас имею сейчас:

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

Который, не только уродливый и медленный, кажется неуклюжим. Однако я не нашел лучшего метода, потому что вызов float в основной функции еще хуже.


Который, не только уродливый и медленный

Я бы обманул обоих.

Регулярное выражение или другой синтаксический анализ строк будет более уродливым и медленным.

Я не уверен, что все может быть быстрее, чем выше. Он вызывает функцию и возвращает. Try / Catch не создает много накладных расходов, потому что наиболее распространенное исключение попадает без широкого поиска фреймов стека.

Проблема в том, что любая функция числового преобразования имеет два вида результатов

  • Число, если номер действителен
  • Код состояния (например, через errno) или исключение, чтобы показать, что действительный номер не может быть проанализирован.

C (как пример) взламывает вокруг этого несколько способов. Python четко и ясно излагает это.

Я думаю, что ваш код для этого идеален.


Который, не только уродливый и медленный, кажется неуклюжим.

Это может занять некоторое время, но это питонический способ сделать это. Как уже отмечалось, альтернативы хуже. Но есть еще одно преимущество в этом: полиморфизм.

Центральная идея утиного печатания заключается в том, что «если он ходит и разговаривает, как утка, то это утка». Что, если вы решите, что вам нужно подклассифицировать строку, чтобы вы могли изменить, как вы определяете, может ли что-то быть преобразовано в float? Или что, если вы решите полностью протестировать какой-либо другой объект? Вы можете делать это без изменения вышеуказанного кода.

Другие языки решают эти проблемы с помощью интерфейсов. Я сохраню анализ того, какое решение лучше для другого потока. Дело в том, что python определенно зависит от утиной печатной стороны уравнения, и вам, вероятно, придется привыкнуть к синтаксису, например, если вы планируете много программировать на Python (но это не значит вам это, конечно, нравится).

Еще одна вещь, которую вы, возможно, захотите принять во внимание: Python довольно быстро бросает и ломает исключения по сравнению со многими другими языками (в 30 раз быстрее, чем .NET). Черт возьми, сам язык даже создает исключения для передачи не исключительных нормальных условий программы (каждый раз, когда вы используете цикл for). Таким образом, я не стал бы слишком беспокоиться об аспектах производительности этого кода, пока вы не заметите существенную проблему.


Ваш код выглядит хорошо для меня.

Возможно, вы считаете, что код «неуклюжий» из-за использования исключений? Обратите внимание, что программисты Python, как правило, предпочитают использовать исключения, когда улучшают читаемость кода, благодаря низкой производительности.



Для строк без номеров try: except: на самом деле медленнее, чем регулярные выражения. Для строк допустимых чисел регулярное выражение работает медленнее. Таким образом, соответствующий метод зависит от вашего ввода.

Если вы обнаружите, что находитесь в fastnumbers к производительности, вы можете использовать новый сторонний модуль, называемый 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: был быстрым для ввода с числовыми данными, но очень медленным для недопустимого ввода
  • regex очень эффективен, когда вход недействителен
  • fastnumbers побед в обоих случаях

Допустим, у вас есть цифры в строке. str = "100949", и вы хотите проверить, есть ли у него только числа

if str.isdigit():
returns TRUE or FALSE 

isdigit docs

в противном случае ваш метод отлично работает, чтобы найти появление цифры в строке.


Есть одно исключение, которое вы можете учесть: строка «NaN»

Если вы хотите, чтобы is_number возвращал FALSE для «NaN», этот код не будет работать, поскольку Python преобразует его в свое представление числа, которое не является числом (говорить о проблемах с идентификацией):

>>> float('NaN')
nan

В противном случае я должен поблагодарить вас за кусок кода, который я сейчас широко использую. :)

Г.


Кастинг для float и catching ValueError, вероятно, самый быстрый способ, поскольку float () специально предназначен именно для этого. Все, что требует синтаксического анализа строк (регулярное выражение и т. Д.), Скорее всего, будет медленнее из-за того, что он не настроен для этой операции. Мои $ 0,02.


Попробуй это.

 def is_number(var):
    try:
       if var == int(var):
            return True
    except Exception:
        return False

Поэтому, чтобы собрать все это, проверив 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

Я знаю, что это особенно старо, но я бы добавил ответ, который, как мне кажется, охватывает информацию, отсутствующую в самом высоком голосовом ответе, который может быть очень ценным для тех, кто находит это:

Для каждого из следующих методов соедините их с подсчетом, если вам нужен какой-либо вход для принятия. (Предполагая, что мы используем голосовые определения целых чисел, а не 0-255 и т. Д.),

x.isdigit() хорошо работает для проверки того, является ли x целым числом.

x.replace('-','').isdigit() хорошо работает для проверки того, является ли x отрицательным. (Check-in в первой позиции)

x.replace('.','').isdigit() хорошо работает для проверки, является ли x десятичным.

x.replace(':','').isdigit() хорошо работает для проверки, является ли x отношением.

x.replace('/','',1).isdigit() работает хорошо для проверки, является ли x дробью.


Я сделал несколько тестов скорости. Предположим, что если строка, вероятно, будет числом, стратегия try / except будет максимально возможной. Если строка вряд ли будет номером, и вас интересует проверка Integer , стоит сделать некоторый тест (isdigit plus heading '-').Если вам интересно проверить число с плавающей запятой, вам нужно использовать escape / исключение кода без выхода.


как насчет этого:

'3.14'.replace('.','',1).isdigit()

который вернет true, только если есть один или нет. в строке цифр.

'3.14.5'.replace('.','',1).isdigit()

вернет false

edit: просто увидели еще один комментарий ... добавление .replace(badstuff,'',maxnum_badstuff) для других случаев может быть выполнено. если вы пропустите соль, а не произвольные приправы (ref: xkcd#974 ), это будет хорошо: P


TL; DR Лучшим решением является s.replace('.','',1).isdigit()

Я сделал несколько benchmarks сравнивающих различные подходы

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 выполняется довольно медленно. Но что более важно, метод 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

EDIT: результаты тестов

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()


РайанН предлагает

Если вы хотите вернуть False для NaN и Inf, измените строку на x = float (s); return (x == x) и (x - 1! = x). Это должно возвращать True для всех поплавков, кроме Inf и NaN

Но это не совсем так, потому что для достаточно больших поплавок x-1 == xвозвращается true. Например,2.0**54 - 1 == 2.0**54


использование после этого обрабатывает все случаи: -

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')

Вы можете обобщить технику исключения полезным образом, возвращая более полезные значения, чем True и False. Например, эта функция ставит кавычки вокруг строк, но оставляет числа в одиночку. Это именно то, что мне нужно для быстрого и грязного фильтра, чтобы сделать некоторые определения переменных для R.

import sys

def fix_quotes(s):
    try:
        float(s)
        return s
    except ValueError:
        return '"{0}"'.format(s)

for line in sys.stdin:
    input = line.split()
    print input[0], '<- c(', ','.join(fix_quotes(c) for c in input[1:]), ')'

Если вы хотите знать, может ли целая строка быть представлена ​​в виде числа, вы хотите использовать regexp (или, возможно, преобразовать float обратно в строку и сравнить ее с исходной строкой, но я предполагаю, что это не очень быстро ).


Я работал над проблемой, которая привела меня к этой теме, а именно, как наиболее интуитивно понятным образом преобразовать коллекцию данных в строки и числа. Я понял, прочитав исходный код, что мне нужно было по-другому:

1 - мне нужен целочисленный результат, если строка представляет целое число

2 - Я хотел, чтобы число или результат строки вставлялись в структуру данных

поэтому я адаптировал исходный код для создания этой производной:

def string_or_number(s):
    try:
        z = int(s)
        return z
    except ValueError:
        try:
            z = float(s)
            return z
        except ValueError:
            return s

Я также использовал функцию, о которой вы упомянули, но вскоре я заметил, что строки как «Нан», «Инф» и ее вариация рассматриваются как число. Поэтому я предлагаю вам улучшенную версию вашей функции, которая вернет false для этих типов ввода и не подведет варианты «1e3»:

def is_float(text):
    try:
        float(text)
        # check for nan/infinity etc.
        if text.isalpha():
            return False
        return True
    except ValueError:
        return False




type-conversion