Як перевірити, чи є рядок числом (float)?


1607

Який найкращий спосіб перевірити, чи може рядок представлений як число в Python?

Зараз у мене функція:

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

Що не тільки некрасиво і повільно, здається незграбним. Однак я не знайшов кращого методу, тому що дзвонити floatв основну функцію ще гірше.


61
Що не так з вашим поточним рішенням? Він короткий, швидкий і читабельний.
Полковник Паніка

5
І вам не просто потрібно повертати Правду чи Неправду. Ви можете замість цього повернути відповідне змінене значення - наприклад, ви можете використовувати це для введення нецифрових лапок у лапки.
Thruston

7
Чи не було б краще повернути результат float (s) у разі успішного перетворення? У вас ще є чек на успішність (результат - помилковий), і ви насправді мають конверсію, яку ви, швидше за все, захочете.
Jiminion

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

7
Не повертайте результат float (s) або None на провал. якщо потім використовувати його так x = float('0.00'); if x: use_float(x);, як тепер у вас є помилка у вашому коді. Значення Truthy є причиною того, що ці функції викликають виняток, а не повертаються Noneв першу чергу. Краще рішення - просто уникнути функції утиліти та оточити виклик, щоб плавати в, try catchколи ви хочете ним користуватися.
ovangle

Відповіді:


698

Що не тільки потворно і повільно

Я б заперечив обоє.

Регекс або інший метод розбору рядків був би гіршим і повільнішим.

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

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

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

C (як приклад) хакує навколо цього різними способами. Python викладає це чітко і чітко.

Я думаю, що ваш код для цього є ідеальним.


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

4
Відповідь здається переконливою, але змушує мене замислитись, чому вона не надається поза коробкою ... Я скопіюю це і використаю його в будь-якому випадку.
шавлія

9
Як жахливо. Як бути, якщо мені байдуже, що це за номер , що це саме число (саме це мене привело сюди)? Замість 1-го рядка IsNumeric()я або закінчую спробу / лову, або іншу завершення спробу / лову. Ugh
Basic

6
Він не надається "поза коробкою", оскільки if is_number(s): x = float(x) else: // failце така ж кількість рядків коду, що і try: x = float(x) catch TypeError: # fail. Ця функція корисності є абсолютно непотрібною абстракцією.
ovangle

12
Але абстракція - це вся суть бібліотек. Наявність функції "isNumber" (будь-якою мовою) допомагає величезній кількості, оскільки ви можете вбудовувати її прямо у висловлювання, але у них є набагато більш читабельний і доступний код, який спирається на блоки "пробування". Крім того, якщо вам потрібно використовувати код більше одного разу в більш ніж одному класі / модулі, тоді ви використовували більше рядків коду, ніж вбудована функція.
JamEngulfer

1612

Якщо ви шукаєте для розбору цілих чисел (позитивних, непідписаних) замість плаваючих, ви можете використовувати isdigit()функцію для рядкових об'єктів.

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

Методи рядків - isdigit(): Python2 , Python3

У рядках Unicode також є щось, що я не дуже знайомий з Unicode - Є десятковим / десятковим


232
Це теж негатив щодо негативів
intrepion

22
Не вдається і з експонентами: '1e3'.isdigit () -> False
ssc

35
Хоча число! = Цифра, люди, які шукають способи перевірити, чи містить рядок ціле число, цілком можуть наткнутися на це питання, і підхід isDigit цілком може бути цілком придатним для їх застосування.
Адам Паркін

8
@AdamParkin: isdigit()і int()мати різні думки щодо того, що таке ціле число, наприклад, для символу Unicode u'\u00b9': u'¹'.isdigit()є, Trueале int(u'¹')підвищує ValueError.
jfs

6
+1: isdigit () може бути не тим, що шукала ОП, але це саме те, чого я хотів. Можливо, це не так, що ця відповідь і метод не охоплюють всіх типів чисел, але вони все ще є дуже актуальними, всупереч аргументам про його точність. Незважаючи на те, що "Число! = Цифра", цифра все ще є підмножиною числа, особливо цифри, які є позитивними, негативними та використовують базу 1-10. Далі цей метод особливо корисний і стислий у випадках, коли ви хочете перевірити, чи є рядок числовим ідентифікатором чи ні, що часто потрапляє в підмножину чисел, яку я щойно описав.
Джастін Джонсон

161

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

Якщо рядок не є числом, виняток-блок є досить повільним. Але що ще важливіше, тестовий метод - єдиний підхід, який правильно обробляє наукові позначення.

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

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


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

Я погоджуюся з @JCChuks: графік дуже допомагає швидко отримати всі TL; DR. Але я думаю, що TL; DR (як-от: TL; DR : найкраще рішення s.replace('.','',1).isdigit()) має з’явитися на початку цього аналізатора. У будь-якому випадку це має бути прийнятим. Дякую!
Simon C.

10
Цей метод не обробляє негативні числа (тире). Я б закликав просто використовувати метод float, оскільки він менш схильний до помилок і буде працювати кожен раз.
їжака

3
Що важливо відзначити, це те, що навіть за припущенням, що не може бути тире, метод заміни-isdigit швидший лише для нечисленних (помилковий результат), тоді як метод спробу виключення швидший для чисел (справжній результат). Якщо більша частина ваших даних є коректним вкладом, вам краще попрацювати з випробувальним рішенням!
Маркус фон

1
Не працює на експоненціальних позначеннях, як '1.5e-9'на негативах.
EL_DON

68

Ви можете взяти до уваги один виняток: рядок "NaN"

Якщо ви хочете, щоб is_number повертав FALSE для 'NaN', цей код не працюватиме, оскільки Python перетворює його у своє представлення числа, яке не є числом (розкажіть про проблеми ідентичності):

>>> float('NaN')
nan

В іншому випадку я дійсно повинен подякувати вам за фрагмент коду, яким зараз широко користуюся. :)

Г.


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

7
Ще один виняток - рядок 'inf'. Або infчи NaNтакож може бути з префіксом +або -й по- , як і раніше приймаються.
agf

4
Якщо ви хочете повернути False для NaN та Inf, змініть рядок на x = float (s); повернення (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()

що повернеться істинним, лише якщо є одне або немає ". у рядку цифр.

'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')повинні зробити кращу роботу з визначення числа (але не всіх, я цього не претендую). Я не рекомендую використовувати це, набагато краще використовувати оригінальний код Запитувача.
Балдрік

якщо вам не подобається це рішення, прочитайте це перед тим, як скасувати!
aloisdg переходить на codidact.com

чоловіче, це найрозумніше рішення, яке я коли-небудь бачив на цьому веб-сайті!
Карам

41

Що не тільки некрасиво і повільно, здається незграбним.

Це може зайняти деяке звикання, але це пітонічний спосіб зробити це. Як уже зазначалося, альтернативи гірші. Але є ще одна перевага робити такі речі: поліморфізм.

Основна ідея типінгу качки полягає в тому, що "якщо вона ходить і розмовляє, як качка, значить, це качка". Що робити, якщо ви вирішите, що вам потрібно підклас рядка, щоб ви могли змінити спосіб визначення того, чи можна щось перетворити в плаваючу? Або що робити, якщо ви вирішили повністю протестувати якийсь інший об’єкт? Ви можете це робити без зміни вищевказаного коду.

Інші мови вирішують ці проблеми за допомогою інтерфейсів. Я збережу аналіз того, яке рішення краще для іншої нитки. Сенс у цьому полягає в тому, що python, безумовно, знаходиться на стороні рівняння качки, і вам, мабуть, доведеться звикнути до такого синтаксису, якщо ви плануєте робити багато програм в Python (але це не означає це вам, звичайно, сподобається).

Ще одне, що ви можете взяти до уваги: ​​Python досить швидко кидає і ловить винятки порівняно з багатьма іншими мовами (на 30 разів швидше, ніж, наприклад, Net). Хрен, сама мова навіть кидає винятки для спілкування не виняткових, нормальних програмних умов (щоразу, коли ви використовуєте цикл). Таким чином, я б не переймався над аспектами продуктивності цього коду, поки ви не помітите значну проблему.


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

Здається, що якщо ви хочете однолінійний, ви SOL
Basic

Також пітонічною є думка про те, що "краще просити прощення, ніж дозволу", щодо впливу дешевих винятків.
heltonbiker

40

Оновлено після того, як Альфе вказав, що вам не потрібно перевіряти на float окремо, як складні ручки:

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().
Альфе

Ця функція поверне значення NaNs та Inf Pandas як числові значення.
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 к. 👏
Зев Авербах

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 ()', вам потрібно буде провести маніпуляцію класом '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.
BlackJack

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запобігло б мою помилку.
Альфе

Ви не проти пояснити, як працює ваш модуль, принаймні для isfloatфункції?
Соломон Учко

@SolomonUcko Ось посилання на вихідний код для перевірки рядка частини: github.com/SethMMorton/fastnumbers/blob/v1.0.0/src / ... . По суті, він проходить через кожний символ у рядку в порядку і перевіряє, що він слідує шаблону для дійсного поплавця. Якщо вхід вже є числом, він просто використовує швидкий PyFloat_Check .
SethMMorton

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()або інакше дати, наприклад 7/7/2017, неправильно трактуються як цифри.
Yuxuan Chen

Найкращі способи зав'язати умови: 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 це дійсний float, що представляє його не числом. Наприклад:

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

Передача в плавати і ловлі ValueError - це, мабуть, найшвидший спосіб, оскільки float () призначений саме для цього. Все, що вимагає розбору рядків (регулярний вираз тощо), швидше за все, буде повільнішим через те, що він не налаштований на цю операцію. Мої 0,02 долара.


11
Ваші долари "2e-2" теж є плаваючим
словом

8
@tzot НІКОЛИ не використовуйте поплавок для представлення грошової вартості.
Лука

6
@Luke: я повністю згоден з вами, хоча я ніколи не пропонував використовувати floats для представлення грошових значень; Я щойно сказав, що грошові цінності можна представити у вигляді поплавків :)
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
для негативних ints це нормально ;-)
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:

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:

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:

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 року і зробив це :)
Рон Рейтер

"Зверніть увагу, що перевірка на успішний склад є єдиним точним методом" <- це насправді не так. Я запустив ваш тест, використовуючи regexp в моїй відповіді вище, і він фактично працює швидше, ніж регулярний. Я додам результати до своєї відповіді вище.
David Ljung Madison Stellar

Між іншим, як кумедний момент, твій творець поганих цифр насправді може створити деякі юридичні номери, хоча це буде досить рідко. :)
David Ljung Madison Stellar

8

Отже, склавши все це разом, перевіривши Нан, нескінченність та складні числа (здавалося б, вони вказані з 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

Поки найкраща відповідь. Спасибі
anish

6

Вхід може бути наступним:

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


1 Загальний вхід:

Вхід цієї функції може бути усім!

Дізнається, чи є дана змінна числовою. Числові рядки складаються з необов’язкового знака, будь-якої кількості цифр, необов'язкової десяткової частини та необов'язкової експоненціальної частини. Таким чином, + 0123.45e6 - дійсне числове значення. Шістнадцяткова (наприклад, 0xf4c3b00c) та двійкові (наприклад, 0b10100111001) позначення заборонені.

is_ числова функція

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- Якщо ви впевнені, що змінна зміст - це String :

використовувати метод 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

Я зробив кілька тестів на швидкість. Скажімо, що якщо рядок, ймовірно, буде числом, то спробуйте / за винятком стратегії - це найшвидше. Якщо рядок, швидше за все, не буде числом, і вас цікавить перевірка 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

Пропонує РайанН

Якщо ви хочете повернути False для NaN та Inf, змініть рядок на x = float (s); повернення (x == x) і (x - 1! = x). Це має повернути True для всіх плавців, крім Inf та NaN

Але це не зовсім спрацьовує, тому що для досить великих поплавків x-1 == xповертається справжнє. Наприклад,2.0**54 - 1 == 2.0**54


3

Я думаю, що ваше рішення добре, але є правильна реагепс-реалізація.

Здається, що у цих відповідях є багато ненависті, що, на мою думку, є невиправданим, регулярні виразки можуть бути досить чистими, правильними та швидкими. Це дійсно залежить від того, що ти намагаєшся зробити. Первісне запитання полягало в тому, як ви можете "перевірити, чи може рядок представлений у вигляді числа (float)" (відповідно до вашої назви). Імовірно, ви хочете використовувати числове / плаваюче значення, як тільки переконаєтесь, що воно дійсне, і в цьому випадку ваша спроба / за винятком має багато сенсу. Але якщо з якоїсь причини ви просто хочете перевірити цей рядок - це числото регулярний вираз також працює чудово, але виправити це важко. Я думаю, що більшість відповідей регулярних виразів досі, наприклад, не розбирають належним чином рядки без цілої частини (наприклад, ".7"), яка є поплавком, що стосується python. І це трохи складно перевірити в одному регексі, де дробова частина не потрібна. Я включив два регулярні вирази, щоб показати це.

Це справді викликає цікаве питання щодо того, що таке "число". Ви включаєте "inf", який дійсний як float у python? Або ви включаєте числа, які є "числами", але, можливо, вони не можуть бути представлені в python (наприклад, числа, що перевищують максимум плавця).

Існують також неоднозначності в аналізі чисел. Наприклад, що з "--20"? Це "число"? Це законний спосіб представити "20"? Python дозволить вам зробити "var = --20" і встановити його на 20 (хоча це дійсно так, тому що він трактує це як вираз), але float ("- 20") не працює.

У будь-якому випадку, без додаткової інформації, ось такий регулярний вираз, який, на мою думку, охоплює всі вбудовані та плаваючі елементи, як пітон розбирає їх .

# 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-Рейтер відповіді показують , що регулярне вираз насправді швидше , ніж нормальні регулярні вирази і набагато швидше при обробці поганих значень , ніж виняток, що робить будь - то сенс. Результати:

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

Сподіваюся, що це правильно - я хотів би почути про будь-які зустрічні приклади. :)
David Ljung Madison Stellar

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, щоб уникнути випадкового спроби виключення, якщо з масивом щось не так.
DarwinSurvivor

1

Я також використовував функцію, яку ви згадали, але незабаром помічаю, що рядки як "Nan", "Inf" і їх варіація вважаються числом. Тому я пропоную вам вдосконалену версію функції, яка поверне помилковий для цього типу введення і не вийде з варіантів "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. Наприклад, ця функція ставить лапки в круглі рядки, але цифри залишає в спокої. Що саме те, що мені знадобилося для швидкого і брудного фільтра, щоб зробити кілька змінних визначень для Р.

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.