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


1611

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

У меня сейчас есть функция:

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

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


61
Что не так с тем, что ваше текущее решение? Это коротко, быстро и читабельно.
Полковник Паник

5
И вам не нужно просто возвращать True или False. Вместо этого вы можете вернуть значение, измененное соответствующим образом - например, вы можете использовать это, чтобы поместить не числа в кавычки.
Трастон

7
Не лучше ли вернуть результат с плавающей точкой в ​​случае успешного преобразования? У вас все еще есть проверка на успех (результат - Ложь), и у вас действительно есть конверсия, которую вы, скорее всего, захотите.
Jiminion

8
Хотя этот вопрос старше, я просто хотел сказать, что это элегантный способ, который задокументирован как EAFP . Так что, вероятно, лучшее решение для такого рода проблем.
Триувенкадам

7
Не возвращайте результат с плавающей точкой или None при ошибке. если вы затем используете его, как x = float('0.00'); if x: use_float(x);теперь у вас есть ошибка в вашем коде. Истинные значения являются причиной, по которой эти функции вызывают исключение, а не возвращаются Noneв первую очередь. Лучшее решение состоит в том, чтобы просто избегать использования служебной функции и заключать вызов в плавающее положение, try catchкогда вы хотите ее использовать.
Ован

Ответы:


699

Что не только уродливо и медленно

Я бы оспорил оба.

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

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

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

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

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

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


21
Я не думаю, что код идеален (но я думаю, что он очень близок): более привычно помещать в предложение только «проверяемую» часть try, поэтому я бы поставил return Trueв elseпредложении условие try. Одна из причин заключается в том, что с кодом в вопросе, если бы мне пришлось его просматривать, мне пришлось бы проверить, что второе утверждение в tryпредложении не может вызвать ValueError: предоставлено, это не требует слишком много времени или умственных способностей, но зачем использовать, когда ничего не нужно?
Эрик О Лебиго

4
Ответ кажется убедительным, но заставляет меня задуматься, почему он не предоставляется "из коробки" ... Я скопирую это и буду использовать в любом случае.
мудрец

9
Как ужасно. Как насчет того, если мне все равно, что это за число, это просто число (которое привело меня сюда)? Вместо 1-й строки IsNumeric()я получаю try / catch или другую упаковку try / catch. Тьфу
Basic

6
Он не предоставляется «из коробки», потому что if is_number(s): x = float(x) else: // failэто то же количество строк кода, что и try: x = float(x) catch TypeError: # fail. Эта служебная функция является совершенно ненужной абстракцией.
Ован

12
Но абстракция - это дело библиотек. Наличие функции isNumber (на любом языке) очень помогает, потому что вы можете встроить ее прямо в операторы if и иметь гораздо более читаемый и поддерживаемый код, который полагается на блоки try - catch. Кроме того, если вам нужно использовать код более одного раза в более чем одном классе / модуле, вы затем использовали больше строк кода, чем встроенная функция.
JamEngulfer

1613

Если вы ищете синтаксические (положительные, беззнаковые) целые числа вместо плавающих, вы можете использовать эту isdigit()функцию для строковых объектов.

>>> a = "03523"
>>> a.isdigit()
True
>>> b = "963spam"
>>> b.isdigit()
False

Строковые методы - isdigit(): Python2 , Python3

Есть также кое-что о строках Unicode, с которыми я не слишком знаком с Unicode - является десятичным / десятичным


232
Это также негатив на негативы
вторжение

22
Сбой также с экспонентами: '1e3'.isdigit () -> False
ssc

35
Хотя Number! = Digit, люди, которые ищут способы проверить, содержит ли строка целое число, могут очень наткнуться на этот вопрос, и подход isDigit вполне может идеально подойти для их применения.
Адам Паркин

8
@AdamParkin: isdigit()и int()имеют разные мнения о том, что такое целое число, например, для символа Unicode u'\u00b9': u'¹'.isdigit()is, Trueно int(u'¹')вызывает ValueError.
JFS

6
+1: isdigit () может быть не тем, что искал ОП, но это именно то, что я хотел. Возможно, это не тот случай, когда этот ответ и метод не охватывают все типы чисел, но он все еще весьма актуален, вопреки аргументам о его точности. Хотя «Number! = Digit», цифра все еще является подмножеством числа, особенно числа, которые являются положительными, неотрицательными и используют основание 1-10. Кроме того, этот метод особенно полезен и краток для случаев, когда вы хотите проверить, является ли строка числовым идентификатором или нет, что часто попадает в подмножество чисел, которое я только что описал.
Джастин Джонсон

162

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

Если строка не является числом, блок исключений работает довольно медленно. Но что еще более важно, метод try-исключения является единственным подходом, который правильно обрабатывает научные записи.

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

введите описание изображения здесь


15
для хороших графиков +1. Я увидел бенчмарк и увидел график, все вещи TL; DR стали понятными и интуитивно понятными.
jcchuks

Я согласен с @JCChuks: график очень помогает быстро получить все TL; DR. Но я думаю, что TL; DR (например, TL; DR : лучшее решение s.replace('.','',1).isdigit()) должно появиться в начале этого ответа. В любом случае это должен быть принятый. Спасибо!
Саймон С.

10
Этот метод не обрабатывает отрицательные числа (тире). Я бы рекомендовал использовать метод float, так как он менее подвержен ошибкам и будет работать каждый раз.
Urchin

3
Важно отметить, что даже при условии, что не может быть тире, метод replace-isdigit быстрее только для не чисел (ложный результат), в то время как метод try-исключения быстрее для чисел (истинный результат). Если большая часть введенных вами данных является верной, лучше воспользоваться решением «попробуй кроме»!
Маркус фон

1
Не работает с экспоненциальной нотацией, как '1.5e-9'или с негативами.
EL_DON

68

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

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

>>> float('NaN')
nan

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

Г.


2
На самом деле, NaNможет быть хорошим значением для возврата (а не False), если переданный текст на самом деле не является представлением числа. Проверять это довольно сложно ( floatтип Python действительно нуждается в методе для этого), но вы можете использовать его в вычислениях, не вызывая ошибки, и нужно только проверить результат.
любезно

7
Другое исключение - строка 'inf'. Либо infили NaNможет быть также с префиксом +или -и до сих пор принимаются.
agf

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

5
x-1 == xверно для больших поплавков меньше, чем inf. В Python 3.2 вы можете использовать math.isfiniteдля проверки числа, которые не являются ни NaN, ни бесконечными, либо проверять и то, math.isnanи другое math.isinf.
Стив Джессоп

56

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

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

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

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

вернет ложь

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


7
Это, однако, не учитывает отрицательные числа.
Майкл Бартон

5
Или числа с показателями типа 1.234e56(которые также могут быть записаны как +1.234E+56и еще несколько вариантов).
Алфе

re.match(r'^[+-]*(0[xbo])?[0-9A-Fa-f]*\.?[0-9A-Fa-f]*(E[+-]*[0-9A-Fa-f]+)$', 'str')следует лучше определить число (но не все, я не претендую на это). Я не рекомендую использовать это, гораздо лучше использовать оригинальный код Вопросника.
Baldrickk

Если вам не нравится это решение, прочитайте это, прежде чем понижать!
aloisdg переходит на codidact.com

Боже, это самое умное решение, которое я когда-либо видел на этом сайте!
Карам Кусай

41

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

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

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

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

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


1
Другое распространенное место, где Python использует исключения для основных функций, - hasattr()это просто getattr()вызов, заключенный в a try/except. Тем не менее, обработка исключений медленнее, чем обычное управление потоком, поэтому использование его для чего-то, что должно быть верным большую часть времени, может привести к снижению производительности.
любезно

Кажется, что если вы хотите однострочник, вы SOL
Basic

Также питонна идея о том, что «лучше просить прощения, чем разрешения» в отношении воздействия дешевых исключений.
heltonbiker

40

Обновлен после того, как 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

14
Я не согласен. Это ОЧЕНЬ маловероятно при обычном использовании, и вам лучше будет создать вызов is_complex_number (), когда вы их используете, а не обременять вызов дополнительной операцией с вероятностью неправильной работы 0,0001%.
Jiminion

3
Вы можете полностью раздеться float()и просто проверить, чтобы complex()звонок был успешным. Все проанализированные float()могут быть проанализированы complex().
Алфе

Эта функция будет возвращать значения Pandas NaNs и Inf в виде числовых значений.
fixxxer

complex('(01989)')вернется (1989+0j). Но не float('(01989)')получится. Поэтому я думаю, что использование complexне очень хорошая идея.
plhn

26

Для 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

2
Не обрабатывает такие вещи, как 1.234e56и похожие. Кроме того, мне было бы интересно, как вы узнаете, что 99999999999999999999e99999999999999999999это не число. Пытаясь разобрать это выясняется быстро.
Алфе

Это работает на ~ 30% быстрее, чем принятое решение для списка из 50-ти строк, и на 150% быстрее для списка из 5-ти строк. Z
Зев Авербах

15

Просто Mimic C #

В 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», потому что это все еще тип значения. Нет лучше, потому что это указывает на неудачу. Конечно, если вы хотите что-то другое, вы можете изменить параметр сбоя на любой другой.

Чтобы расширить 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

И великий мудрец Питонас сказал Святейшему Престолу Шарпису: «Все, что ты можешь сделать, я могу делать лучше, я могу делать все лучше тебя».


В последнее время я кодировал в основном JS и фактически не тестировал это, поэтому могут быть небольшие ошибки. Если вы видите, не стесняйтесь исправлять мои ошибки.
Эван Плейс

Чтобы добавить поддержку комплексных чисел, смотрите ответ @Matthew Wilcoxson. stackoverflow.com/a/3335060/290340 .
Эван Плейс

1
Использование !вместо notможет быть незначительной ошибкой, но вы определенно не можете назначить атрибуты встроенному floatв CPython.
Блэкджек

15

Для строк не чисел, 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 выигрывает в обоих случаях

Я поправляюсь: -} это просто не выглядело так, как будто он это делал. Возможно использование имен как prep_code_basisи prep_code_re_methodпредотвратило бы мою ошибку.
Alfe

Не могли бы вы объяснить, как работает ваш модуль, хотя бы для isfloatфункции?
Соломон Уцко

@SolomonUcko Вот ссылка на исходный код для проверки строки: github.com/SethMMorton/fastnumbers/blob/v1.0.0/src/… . По сути, он проходит по каждому символу в строке по порядку и проверяет, что он следует шаблону для допустимого числа с плавающей запятой. Если входное значение уже является числом, оно просто использует быстрый PyFloat_Check .
СетМортон

1
Испытано против лучших альтернатив в этой теме я подтверждаю это решение намного быстрее. Второй самый быстрый метод - str(s).strip('-').replace('.','',1).isdigit()это примерно в 10 раз медленнее!
Александр Макфарлейн

14

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

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

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

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

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

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

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


1
Хотя для дробей вам, вероятно, нужно сделать, x.replace('/','',1).isdigit()иначе даты, такие как 4/7/2017, будут неверно истолкованы как числа.
Юйсуань Чен

Для лучших способов связать условия: stackoverflow.com/q/3411771/5922329
Даниэль Браун

13

Этот ответ предоставляет пошаговое руководство, имеющее функцию с примерами для поиска строки:

  • Положительное число
  • Положительный / отрицательный - целое число / число с плавающей запятой
  • Как отбросить строки "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 это допустимое число с плавающей точкой, представляющее, что это не число. Например:

>>> is_number('NaN')
True

Чтобы проверить, является ли число «NaN», вы можете использовать math.isnan()как:

>>> import math
>>> nan_num = float('nan')

>>> math.isnan(nan_num)
True

Или, если вы не хотите импортировать дополнительную библиотеку, чтобы проверить это, вы можете просто проверить ее, сравнив ее с самой собой, используя == . Python возвращается, Falseкогда nanfloat сравнивается с самим собой. Например:

# `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функции, которая соответствует вашим требованиям.


12

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


11
Ваши «2e-2» доллары тоже являются float (дополнительный аргумент для использования float :)
tzot

8
@tzot НИКОГДА не используйте float для представления денежного значения.
Люк

6
@Luke: Я полностью согласен с вами, хотя я никогда не предлагал использовать числа с плавающей точкой для представления денежных величин; Я только что сказал, что денежные значения могут быть представлены как поплавки :)
tzot

11

Вы можете использовать строки 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


2
для неотрицательных целых это нормально ;-)
andilabs

1
s.isdecimal()проверяет, является ли sстрока неотрицательным целым числом. s.isnumeric()включает в себя символы, которые int()отклоняет.
JFS

9

Я хотел посмотреть, какой метод самый быстрый. В целом, лучшие и наиболее последовательные результаты были получены check_replaceфункцией. Самые быстрые результаты далиcheck_exception функцией, но только в том случае, если не было сгенерировано исключение - это означает, что ее код является наиболее эффективным, но накладные расходы на создание исключения довольно велики.

Обратите внимание, что проверка на успешное приведение является единственным методом, который является точным, например, он работает, check_exceptionно две другие тестовые функции вернут 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))

Вот результаты с Python 2.7.10 на MacBook Pro 13 2017 года:

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

Вот результаты с Python 3.6.5 на MacBook Pro 13 2017 года:

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

Вот результаты с PyPy 2.7.13 на MacBook Pro 13 2017 года:

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

10
Вы также должны проверить производительность для недопустимых случаев. С этими числами не возникает никаких исключений, что является «медленной» частью.
Ugo Méda

1
@ UgoMéda Я взял твой совет с 2013 года и сделал это :)
Ron Reiter

«Обратите внимание, что проверка на успешное приведение - единственный метод, который является точным» <- это на самом деле не так. Я выполнил ваш тест с использованием регулярного выражения в моем ответе выше, и он на самом деле работает быстрее, чем регулярное выражение. Я добавлю результаты в мой ответ выше.
Дэвид Люнг Мэдисон Стеллар

Кстати, как забавный момент, ваш создатель плохих чисел может на самом деле создать несколько допустимых чисел, хотя это будет довольно редко. :)
Дэвид Люнг Мэдисон Стеллар

8

Таким образом, чтобы сложить все вместе, проверяя на 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

Пока лучший ответ. Спасибо
аниш

6

Ввод может быть следующим:

a="50" b=50 c=50.1 d="50.1"


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

что такое аст ?


2- Если вы уверены, что переменным содержимым является строка :

Использование str.isdigit () метод

>>> a=454
>>> a.isdigit()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'int' object has no attribute 'isdigit'
>>> a="454"
>>> a.isdigit()
True

3-числовой вход:

определить значение int:

>>> isinstance("54", int)
False
>>> isinstance(54, int)
True
>>> 

обнаружить поплавок:

>>> isinstance("45.1", float)
False
>>> isinstance(45.1, float)
True

что такое " ast"?

4

Я сделал тест скорости. Допустим , что если строка скорее всего будет несколько попробовать / за исключением стратегии является самым possible.If строка не вероятно , чтобы быть числом , и вы заинтересованы в Integer чеком, Вортс сделать некоторые испытания (isdigit плюс заголовок '-'). Если вы заинтересованы в проверке числа с плавающей запятой, вы должны использовать пробный / исключающий код без выхода.


4

Мне нужно было определить, приведена ли строка к базовым типам (float, int, str, bool). Не найдя ничего в интернете, я создал это:

def str_to_type (s):
    """ Get possible cast type for a string

    Parameters
    ----------
    s : string

    Returns
    -------
    float,int,str,bool : type
        Depending on what it can be cast to

    """    
    try:                
        f = float(s)        
        if "." not in s:
            return int
        return float
    except ValueError:
        value = s.upper()
        if value == "TRUE" or value == "FALSE":
            return bool
        return type(s)

пример

str_to_type("true") # bool
str_to_type("6.0") # float
str_to_type("6") # int
str_to_type("6abc") # str
str_to_type(u"6abc") # unicode       

Вы можете захватить тип и использовать его

s = "6.0"
type_ = str_to_type(s) # float
f = type_(s) 

3

RyanN предлагает

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

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


3

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

Похоже, в этих ответах есть много ненависти к регулярным выражениям, которые я считаю неоправданными, регулярные выражения могут быть достаточно чистыми, правильными и быстрыми. Это действительно зависит от того, что вы пытаетесь сделать. Первоначальный вопрос заключался в том, как можно «проверить, можно ли представить строку в виде числа (с плавающей запятой)» (согласно заголовку). Предположительно, вы захотите использовать числовое значение / значение с плавающей запятой после того, как вы проверите, что оно допустимо, и в этом случае ваши попытки / исключения имеют большой смысл. Но если по какой-то причине вы просто хотите проверить, что строка является числомтогда регулярное выражение также работает нормально, но его трудно получить правильно. Я думаю, что большинство ответов на регулярные выражения до сих пор, например, неправильно анализируют строки без целочисленной части (такой как ".7"), которая является плавающей точкой в ​​том, что касается python. И это немного сложно проверить в одном регулярном выражении, где дробная часть не требуется. Я включил два регулярных выражения, чтобы показать это.

Это поднимает интересный вопрос о том, что такое «число». Вы включаете "inf", который является допустимым как float в python? Или вы включаете числа, которые являются «числами», но, возможно, не могут быть представлены в Python (например, числа, которые больше, чем максимум с плавающей точкой).

Есть также неясности в том, как вы анализируете числа. Например, как насчет "--20"? Это «число»? Это законный способ представлять «20»? Python позволит вам сделать «var = --20» и установить его в 20 (хотя на самом деле это потому, что он обрабатывает это как выражение), но float («- 20») не работает.

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

# Doesn't properly handle floats missing the integer part, such as ".7"
SIMPLE_FLOAT_REGEXP = re.compile(r'^[-+]?[0-9]+\.?[0-9]+([eE][-+]?[0-9]+)?$')
# Example "-12.34E+56"      # sign (-)
                            #     integer (12)
                            #           mantissa (34)
                            #                    exponent (E+56)

# Should handle all floats
FLOAT_REGEXP = re.compile(r'^[-+]?([0-9]+|[0-9]*\.[0-9]+)([eE][-+]?[0-9]+)?$')
# Example "-12.34E+56"      # sign (-)
                            #     integer (12)
                            #           OR
                            #             int/mantissa (12.34)
                            #                            exponent (E+56)

def is_float(str):
  return True if FLOAT_REGEXP.match(str) else False

Некоторые примеры тестовых значений:

True  <- +42
True  <- +42.42
False <- +42.42.22
True  <- +42.42e22
True  <- +42.42E-22
False <- +42.42e-22.8
True  <- .42
False <- 42nope

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

check_regexp with good floats: 18.001921
check_regexp with bad floats: 17.861423
check_regexp with strings: 17.558862
check_correct_regexp with good floats: 11.04428
check_correct_regexp with bad floats: 8.71211
check_correct_regexp with strings: 8.144161
check_replace with good floats: 6.020597
check_replace with bad floats: 5.343049
check_replace with strings: 5.091642
check_exception with good floats: 5.201605
check_exception with bad floats: 23.921864
check_exception with strings: 23.755481

Надеюсь, что это правильно - хотелось бы услышать о любых контрпримеры. :)
Дэвид Люнг Мэдисон

2
import re
def is_number(num):
    pattern = re.compile(r'^[-+]?[-0-9]\d*\.\d*|[-+]?\.?[0-9]\d*$')
    result = pattern.match(num)
    if result:
        return True
    else:
        return False


​>>>: is_number('1')
True

>>>: is_number('111')
True

>>>: is_number('11.1')
True

>>>: is_number('-11.1')
True

>>>: is_number('inf')
False

>>>: is_number('-inf')
False

2
Вы не считаете, 1e6чтобы представлять число?
Марк Дикинсон

1

Вот мой простой способ сделать это. Допустим, я перебираю некоторые строки и хочу добавить их в массив, если они окажутся числами.

try:
    myvar.append( float(string_to_check) )
except:
    continue

Замените myvar.apppend любой операцией, которую вы хотите выполнить со строкой, если она окажется числом. Идея состоит в том, чтобы попытаться использовать операцию float () и использовать возвращенную ошибку, чтобы определить, является ли строка числом.


Вы должны переместить часть добавления этой функции в оператор else, чтобы избежать случайного запуска исключения, если с массивом что-то не так.
ДарвинСурвивор

1

Я также использовал функцию, которую вы упомянули, но вскоре я заметил, что строки как «Nan», «Inf» и их вариация считаются числом. Поэтому я предлагаю вам улучшенную версию вашей функции, которая будет возвращать 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

1

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

return True if str1.lstrip('-').replace('.','',1).isdigit() or float(str1) else False

1

Пользовательская вспомогательная функция:

def if_ok(fn, string):
  try:
    return fn(string)
  except Exception as e:
    return None

тогда

if_ok(int, my_str) or if_ok(float, my_str) or if_ok(complex, my_str)
is_number = lambda s: any([if_ok(fn, s) for fn in (int, float, complex)])

0

Вы можете обобщить технику исключений полезным способом, возвращая больше полезных значений, чем 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:]), ')'

0

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

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


0
def is_float(s):
    if s is None:
        return False

    if len(s) == 0:
        return False

    digits_count = 0
    dots_count = 0
    signs_count = 0

    for c in s:
        if '0' <= c <= '9':
            digits_count += 1
        elif c == '.':
            dots_count += 1
        elif c == '-' or c == '+':
            signs_count += 1
        else:
            return False

    if digits_count == 0:
        return False

    if dots_count > 1:
        return False

    if signs_count > 1:
        return False

    return True
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.