Проверка возможности преобразования строки в число с плавающей запятой в Python
-
09-09-2019 - |
Вопрос
У меня есть код 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 и посмотреть, удастся ли оно, как описано в разделе этот вопрос.
У кого-нибудь есть другие идеи?Мнения об относительных достоинствах подходов разделения и «попробуй/поймай»?
Решение
Я бы просто использовал..
try:
float(element)
except ValueError:
print "Not a float"
...это просто, и это работает
Другим вариантом может быть регулярное выражение:
import re
if re.match("^\d+?\.\d+?$", element) is None:
print "Not float"
Другие советы
Метод Python для проверки плавающей точки:
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
'1.43'.replace('.','',1).isdigit()
который вернется true
Только если есть один или нет ». в строке цифр.
'1.4.3'.replace('.','',1).isdigit()
вернется false
'1.ww'.replace('.','',1).isdigit()
вернется false
Если вас заботит производительность (а я не предлагаю вам это делать), подход, основанный на попытках, является явным победителем (по сравнению с подходом на основе секций или подходом с регулярными выражениями), если вы не ожидаете многого недопустимые строки, и в этом случае это потенциально медленнее (предположительно из-за стоимости обработки исключений).
Опять же, я не предлагаю вам заботиться о производительности, я просто предоставляю вам данные на случай, если вы делаете это 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, регулярное выражение взято из книги Джона Гитцена отвечать):
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()
ТЛ;ДР:
- Если ваш ввод состоит в основном из строк, которые может быть преобразованы в числа с плавающей запятой,
try: except:
метод — лучший собственный метод Python. - Если ваш ввод состоит в основном из строк, которые не могу быть преобразованы в числа с плавающей запятой, регулярные выражения или метод разделения будет лучше.
- Если вы 1) не уверены в правильности ввода или вам нужна большая скорость и 2) вы не возражаете и можете установить стороннее C-расширение, быстрые номера работает очень хорошо.
Существует еще один метод, доступный через сторонний модуль под названием быстрые номера (разглашение, я автор);он предоставляет функцию под названием плавает.Я взял пример 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
метод вполне хороший.
Просто для разнообразия вот еще один способ сделать это.
>>> all([i.isnumeric() for i in '1.2'.split('.',1)])
True
>>> all([i.isnumeric() for i in '2'.split('.',1)])
True
>>> all([i.isnumeric() for i in '2.f'.split('.',1)])
False
Редактировать:Я уверен, что он не выдержит всех случаев с плавающей запятой, особенно когда есть показатель степени.Чтобы решить эту проблему, это выглядит так.Это вернет только True, val — это число с плавающей запятой, а False — для int, но, вероятно, менее производительно, чем регулярное выражение.
>>> def isfloat(val):
... return all([ [any([i.isnumeric(), i in ['.','e']]) for i in val], len(val.split('.')) == 2] )
...
>>> isfloat('1')
False
>>> isfloat('1.2')
True
>>> isfloat('1.2e3')
True
>>> isfloat('12e3')
False
Это регулярное выражение проверит наличие научных чисел с плавающей запятой:
^[-+]?(?:\b[0-9]+(?:\.[0-9]*)?|\.[0-9]+\b)(?:[eE][-+]?[0-9]+\b)?$
Однако я считаю, что лучше всего попробовать использовать парсер.
Если вам не нужно беспокоиться о научных или других выражениях чисел и вы работаете только со строками, которые могут быть числами с точкой или без нее:
Функция
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, в число с плавающей запятой.
Упрощенная версия функции is_digit(str)
, чего достаточно в большинстве случаев (не учитывает экспоненциальная запись и "НаН" ценить):
def is_digit(str):
return str.lstrip('-').replace('.', '').isdigit()
Я использовал уже упомянутую функцию, но вскоре заметил, что строки типа «Nan», «Inf» и их вариации считаются числом.Поэтому я предлагаю вам улучшенную версию функции, которая будет возвращать false для этих типов ввода и не будет выдавать ошибки «1e3»:
def is_float(text):
# check for nan/infinity etc.
if text.isalpha():
return False
try:
float(text)
return True
except ValueError:
return False
Попробуйте преобразовать в float.Если возникла ошибка, выведите исключение ValueError.
try:
x = float('1.23')
print('val=',x)
y = float('abc')
print('val=',y)
except ValueError as err:
print('floatErr;',err)
Выход:
val= 1.23
floatErr: could not convert string to float: 'abc'
Я искал аналогичный код, но, похоже, лучше всего использовать try/Exceptions.Вот код, который я использую.Он включает функцию повтора, если введенные данные недействительны.Мне нужно было проверить, было ли введенное значение больше 0, и если да, преобразовать его в число с плавающей запятой.
def cleanInput(question,retry=False):
inputValue = input("\n\nOnly positive numbers can be entered, please re-enter the value.\n\n{}".format(question)) if retry else input(question)
try:
if float(inputValue) <= 0 : raise ValueError()
else : return(float(inputValue))
except ValueError : return(cleanInput(question,retry=True))
willbefloat = cleanInput("Give me the number: ")
str(strval).isdigit()
кажется, что все просто.
Обрабатывает значения, хранящиеся в виде строки, целого числа или числа с плавающей запятой.