Проверка, может ли строка быть преобразована в float в Python

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

if element.isdigit():
  newelement = int(element)

Числа с плавающей запятой сложнее. Прямо сейчас я использую partition('.') для разделения строки и проверки, чтобы убедиться, что одна или обе стороны являются цифрами.

partition = element.partition('.')
if (partition[0].isdigit() and partition[1] == '.' and partition[2].isdigit()) 
    or (partition[0] == '' and partition[1] == '.' and partition[2].isdigit()) 
    or (partition[0].isdigit() and partition[1] == '.' and partition[2] == ''):
  newelement = float(element)

Это работает, но очевидно, что оператор if для этого - немного медведя. Другое решение, которое я рассмотрел, - это просто конвертировать конверсию в блок try/catch и посмотреть, удастся ли ему выполнить, как описано в этом вопросе.

У кого-нибудь есть другие идеи? Мнения относительно относительных достоинств раздела и попыток/подходов?

Ответ 1

Я бы просто использовал..

try:
    float(element)
except ValueError:
    print "Not a float"

.. он прост, и он работает

Другим вариантом будет регулярное выражение:

import re
if re.match("^\d+?\.\d+?$", element) is None:
    print "Not float"

Ответ 2

Метод Python для проверки float:

def isfloat(value):
  try:
    float(value)
    return True
  except ValueError:
    return False

Не бойся гоблинов, скрывающихся в поплавковой лодке! ПРОВЕРЬТЕ ТЕСТИРОВАНИЕ УСТРОЙСТВА!

Что есть, и не плавающий может вас удивить:

Command to parse                        Is it a float?  Comment
--------------------------------------  --------------- ------------
print(isfloat(""))                      False
print(isfloat("1234567"))               True 
print(isfloat("NaN"))                   True            nan is also float
print(isfloat("NaNananana BATMAN"))     False
print(isfloat("123.456"))               True
print(isfloat("123.E4"))                True
print(isfloat(".1"))                    True
print(isfloat("1,234"))                 False
print(isfloat("NULL"))                  False           case insensitive
print(isfloat(",1"))                    False           
print(isfloat("123.EE4"))               False           
print(isfloat("6.523537535629999e-07")) True
print(isfloat("6e777777"))              True            This is same as Inf
print(isfloat("-iNF"))                  True
print(isfloat("1.797693e+308"))         True
print(isfloat("infinity"))              True
print(isfloat("infinity and BEYOND"))   False
print(isfloat("12.34.56"))              False           Two dots not allowed.
print(isfloat("#56"))                   False
print(isfloat("56%"))                   False
print(isfloat("0E0"))                   True
print(isfloat("x86E0"))                 False
print(isfloat("86-5"))                  False
print(isfloat("True"))                  False           Boolean is not a float.   
print(isfloat(True))                    True            Boolean is a float
print(isfloat("+1e1^5"))                False
print(isfloat("+1e1"))                  True
print(isfloat("+1e1.3"))                False
print(isfloat("+1.3P1"))                False
print(isfloat("-+1"))                   False
print(isfloat("(1)"))                   False           brackets not interpreted

Ответ 3

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

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

$ ./floatstr.py
F..
partition sad: 3.1102449894
partition happy: 2.09208488464
..
re sad: 7.76906108856
re happy: 7.09421992302
..
try sad: 12.1525540352
try happy: 1.44165301323
.
======================================================================
FAIL: test_partition (__main__.ConvertTests)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "./floatstr.py", line 48, in test_partition
    self.failUnless(is_float_partition("20e2"))
AssertionError

----------------------------------------------------------------------
Ran 8 tests in 33.670s

FAILED (failures=1)

Здесь код (Python 2.6, regexp, взятый из John Gietzen answer):

def is_float_try(str):
    try:
        float(str)
        return True
    except ValueError:
        return False

import re
_float_regexp = re.compile(r"^[-+]?(?:\b[0-9]+(?:\.[0-9]*)?|\.[0-9]+\b)(?:[eE][-+]?[0-9]+\b)?$")
def is_float_re(str):
    return re.match(_float_regexp, str)


def is_float_partition(element):
    partition=element.partition('.')
    if (partition[0].isdigit() and partition[1]=='.' and partition[2].isdigit()) or (partition[0]=='' and partition[1]=='.' and pa\
rtition[2].isdigit()) or (partition[0].isdigit() and partition[1]=='.' and partition[2]==''):
        return True

if __name__ == '__main__':
    import unittest
    import timeit

    class ConvertTests(unittest.TestCase):
        def test_re(self):
            self.failUnless(is_float_re("20e2"))

        def test_try(self):
            self.failUnless(is_float_try("20e2"))

        def test_re_perf(self):
            print
            print 're sad:', timeit.Timer('floatstr.is_float_re("12.2x")', "import floatstr").timeit()
            print 're happy:', timeit.Timer('floatstr.is_float_re("12.2")', "import floatstr").timeit()

        def test_try_perf(self):
            print
            print 'try sad:', timeit.Timer('floatstr.is_float_try("12.2x")', "import floatstr").timeit()
            print 'try happy:', timeit.Timer('floatstr.is_float_try("12.2")', "import floatstr").timeit()

        def test_partition_perf(self):
            print
            print 'partition sad:', timeit.Timer('floatstr.is_float_partition("12.2x")', "import floatstr").timeit()
            print 'partition happy:', timeit.Timer('floatstr.is_float_partition("12.2")', "import floatstr").timeit()

        def test_partition(self):
            self.failUnless(is_float_partition("20e2"))

        def test_partition2(self):
            self.failUnless(is_float_partition(".2"))

        def test_partition3(self):
            self.failIf(is_float_partition("1234x.2"))

    unittest.main()

Ответ 4

TL; DR:

  • Если ваш ввод - это в основном строки, которые можно преобразовать в float, метод try: except: является лучшим нативным методом Python.
  • Если ваш ввод - это в основном строки, которые нельзя преобразовать в поплавки, регулярные выражения или метод разделения будут лучше.
  • Если вы 1) не уверены в своем вводе или нуждаетесь в большей скорости и 2) не возражаете и можете установить стороннее C-расширение, fastnumbers работает очень хорошо.

Существует другой метод, доступный через сторонний модуль под названием fastnumbers (раскрытие, я являюсь автором); он предоставляет функцию под названием isfloat. Я принял пример unittest, описанный Яковом Габриэльсоном в этом ответе, но добавил метод fastnumbers.isfloat. Следует также отметить, что пример Джейкоба не оправдывал вариант регулярного выражения, потому что большую часть времени в этом примере тратили на глобальные поиски из-за оператора точек... Я изменил эту функцию, чтобы дать более справедливое сравнение с try: except:,


def is_float_try(str):
    try:
        float(str)
        return True
    except ValueError:
        return False

import re
_float_regexp = re.compile(r"^[-+]?(?:\b[0-9]+(?:\.[0-9]*)?|\.[0-9]+\b)(?:[eE][-+]?[0-9]+\b)?$").match
def is_float_re(str):
    return True if _float_regexp(str) else False

def is_float_partition(element):
    partition=element.partition('.')
    if (partition[0].isdigit() and partition[1]=='.' and partition[2].isdigit()) or (partition[0]=='' and partition[1]=='.' and partition[2].isdigit()) or (partition[0].isdigit() and partition[1]=='.' and partition[2]==''):
        return True
    else:
        return False

from fastnumbers import isfloat


if __name__ == '__main__':
    import unittest
    import timeit

    class ConvertTests(unittest.TestCase):

        def test_re_perf(self):
            print
            print 're sad:', timeit.Timer('ttest.is_float_re("12.2x")', "import ttest").timeit()
            print 're happy:', timeit.Timer('ttest.is_float_re("12.2")', "import ttest").timeit()

        def test_try_perf(self):
            print
            print 'try sad:', timeit.Timer('ttest.is_float_try("12.2x")', "import ttest").timeit()
            print 'try happy:', timeit.Timer('ttest.is_float_try("12.2")', "import ttest").timeit()

        def test_fn_perf(self):
            print
            print 'fn sad:', timeit.Timer('ttest.isfloat("12.2x")', "import ttest").timeit()
            print 'fn happy:', timeit.Timer('ttest.isfloat("12.2")', "import ttest").timeit()


        def test_part_perf(self):
            print
            print 'part sad:', timeit.Timer('ttest.is_float_partition("12.2x")', "import ttest").timeit()
            print 'part happy:', timeit.Timer('ttest.is_float_partition("12.2")', "import ttest").timeit()

    unittest.main()

На моей машине вывод:

fn sad: 0.220988988876
fn happy: 0.212214946747
.
part sad: 1.2219619751
part happy: 0.754667043686
.
re sad: 1.50515985489
re happy: 1.01107215881
.
try sad: 2.40243887901
try happy: 0.425730228424
.
----------------------------------------------------------------------
Ran 4 tests in 7.761s

OK

Как вы можете видеть, регулярное выражение на самом деле не так плохо, как казалось изначально, и если вам действительно нужна скорость, метод fastnumbers неплох.

Ответ 5

Это регулярное выражение будет проверять научные числа с плавающей запятой:

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

Однако я считаю, что лучше всего использовать синтаксический анализатор в попытке.

Ответ 6

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

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

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

вернет false

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

вернет false

Ответ 7

Если вам не нужно беспокоиться о научных или других выражениях чисел и работать со строками, которые могут быть числами с периодом или без него:

Функция

def is_float(s):
    result = False
    if s.count(".") == 1:
        if s.replace(".", "").isdigit():
            result = True
    return result

Лямбда версия

is_float = lambda x: x.replace('.','',1).isdigit() and "." in x

Пример

if is_float(some_string):
    some_string = float(some_string)
elif some_string.isdigit():
    some_string = int(some_string)
else:
    print "Does not convert to int or float."

Таким образом, вы не случайно конвертируете то, что должно быть int, в float.

Ответ 8

Я использовал уже упомянутую функцию, но вскоре я заметил, что строки как "Нан", "Инф" и ее вариация рассматриваются как число. Поэтому я предлагаю вам улучшенную версию функции, которая вернет 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

Ответ 9

str (strval).isdigit() кажется простым. Обрабатывает значения, хранящиеся в виде строки или int или float