J'ai du code Python qui parcourt une liste de chaînes et les convertit en nombres entiers ou en nombres à virgule flottante si possible. Faire cela pour les entiers est assez facile
if element.isdigit():
newelement = int(element)
Les nombres en virgule flottante sont plus difficiles. En ce moment, j'utilise partition('.')
pour scinder la chaîne et vérifie que les deux côtés sont des chiffres.
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)
Cela fonctionne, mais évidemment, la déclaration if est un peu un ours. L'autre solution que j'ai envisagée consiste à envelopper la conversion dans un bloc try/catch et à voir si elle réussit, comme décrit dans cette question .
Quelqu'un a d'autres idées? Opinions sur les mérites relatifs de la partition et des approches try/catch?
Je voudrais juste utiliser ..
try:
float(element)
except ValueError:
print "Not a float"
..c'est simple, et ça marche
Une autre option serait une expression régulière:
import re
if re.match("^\d+?\.\d+?$", element) is None:
print "Not float"
def isfloat(value):
try:
float(value)
return True
except ValueError:
return False
Ce qui est et n’est pas un char peut vous surprendre:
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()
qui retournera true
seulement s'il y en a un ou pas '.' dans la chaîne de chiffres .
'1.4.3'.replace('.','',1).isdigit()
retournera false
'1.ww'.replace('.','',1).isdigit()
retournera false
Si vous vous souciez de la performance (et je ne vous le suggère pas), l’approche basée sur l’essai est clairement gagnante (comparée à votre approche basée sur les partitions ou à l’approche regexp), tant que vous ne vous attendez pas à beaucoup de choses. chaînes non valides, auquel cas il est potentiellement plus lent (probablement en raison du coût de la gestion des exceptions).
Encore une fois, je ne dis pas que vous vous souciez de la performance, mais simplement de vous fournir les données au cas où vous le feriez 10 milliards de fois par seconde, ou quelque chose du genre. En outre, le code basé sur la partition ne gère pas au moins une chaîne valide.
$, ... try sad: 12.1525540352 try happy: 1.44165301323 . ========================= ==========================================. FAIL: test_partition (__main __. ConvertTests) ---------------------------------------------- ---------------------------- Traceback (dernier appel passé): Fichier "./floatstr.py", ligne 48, dans test_partition self.failUnless (is_float_partition ("20e2")) AssertionError ----------------------------- ----------------------------------------- A couru 8 tests en 33.670s FAILED (échecs = 1)
Voici le code (Python 2.6, expression rationnelle extraite de/Giunzen's 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 __== '__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()
TL; DR:
try: except:
est la meilleure méthode Python native.Une autre méthode est disponible via un module tiers appelé fastnumbers (divulgation, je suis l’auteur); il fournit une fonction appelée isfloat . J'ai pris l'exemple le plus incertain décrit par Jacob Gabrielson dans cette réponse , mais en ajoutant la méthode fastnumbers.isfloat
. Je devrais également noter que l'exemple de Jacob ne rend pas justice à l'option regex, car la plupart du temps, cet exemple est consacré aux recherches globales à cause de l'opérateur point ... J'ai modifié cette fonction pour permettre une comparaison plus juste avec 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 __== '__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()
Sur ma machine, la sortie est:
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
Comme vous pouvez le constater, l’expression rationnelle n’est en réalité pas aussi mauvaise qu’elle le semblait à l’origine, et si vous avez un réel besoin de rapidité, la méthode fastnumbers
est assez bonne.
Juste pour la variété, voici une autre méthode pour le faire.
>>> 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
Edit: Je suis sûr que cela ne tiendra pas à tous les cas de float, surtout s'il y a un exposant. Pour résoudre cela, il ressemble à ceci. Cela retournera True uniquement val est un float et False pour int mais est probablement moins performant que regex.
>>> 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
Si vous n'avez pas à vous soucier d'expressions de chiffres scientifiques ou autres et que vous travaillez uniquement avec des chaînes pouvant être des nombres avec ou sans point:
Une fonction
def is_float(s):
result = False
if s.count(".") == 1:
if s.replace(".", "").isdigit():
result = True
return result
Version Lambda
is_float = lambda x: x.replace('.','',1).isdigit() and "." in x
Exemple
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."
De cette façon, vous ne convertissez pas accidentellement ce qui devrait être un int, un float.
Cette expression régulière recherchera des nombres à virgule flottante scientifiques:
^[-+]?(?:\b[0-9]+(?:\.[0-9]*)?|\.[0-9]+\b)(?:[eE][-+]?[0-9]+\b)?$
Cependant, je crois que votre meilleur pari est d’utiliser l’analyseur lors d’un essai.
Essayez de convertir pour flotter. En cas d'erreur, imprimez l'exception ValueError.
try:
x = float('1.23')
print('val=',x)
y = float('abc')
print('val=',y)
except ValueError as err:
print('floatErr;',err)
Sortie:
val= 1.23
floatErr: could not convert string to float: 'abc'
J'ai utilisé la fonction déjà mentionnée, mais j'ai vite remarqué que les chaînes telles que "Nan", "Inf" et leurs variations sont considérées comme des nombres. Je vous propose donc une version améliorée de la fonction, qui renverra false sur ce type d'entrée et n'échouera pas aux variantes "1e3":
def is_float(text):
# check for nan/infinity etc.
if text.isalpha():
return False
try:
float(text)
return True
except ValueError:
return False
Je cherchais du code similaire, mais il semble que l'utilisation de try/excepts soit la meilleure solution… .. Voici le code que j'utilise. Il inclut une fonction de nouvelle tentative si l'entrée n'est pas valide. Je devais vérifier si l’entrée était supérieure à 0 et, le cas échéant, la convertir en float.
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()
semble être simple.
Traite les valeurs stockées sous forme de chaîne, int ou float
Version simplifiée de la fonction is_digit(str)
, qui suffit dans la plupart des cas (ne considère pas la notation exponentielle et la valeur "NaN"):
def is_digit(str):
return str.lstrip('-').replace('.', '').isdigit()