Checking if a string can be converted to float in Python - python
I've got some Python code that runs through a list of strings and converts them to integers or floating point numbers if possible. Doing this for integers is pretty easy
if element.isdigit():
newelement = int(element)
Floating point numbers are more difficult. Right now I'm using partition('.') to split the string and checking to make sure that one or both sides are digits.
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)
This works, but obviously the if statement for that is a bit of a bear. The other solution I considered is to just wrap the conversion in a try/catch block and see if it succeeds, as described in this question.
Anyone have any other ideas? Opinions on the relative merits of the partition and try/catch approaches?
I would just use..
try:
float(element)
except ValueError:
print "Not a float"
..it's simple, and it works. Note that it will still throw OverflowError if element is e.g. 1<<1024.
Another option would be a regular expression:
import re
if re.match(r'^-?\d+(?:\.\d+)$', element) is None:
print "Not float"
Python3 method to check for float:
def is_float(element: any) -> bool:
#If you expect None to be passed:
if element is None:
return False
try:
float(element)
return True
except ValueError:
return False
Python2 version of the above: How do I parse a string to a float or int?
Always do unit testing. What is and is not a float may surprise you:
Command to parse Is it a float? Comment
-------------------------------------- --------------- ------------
print(isfloat("")) False
print(isfloat("1234567")) True
print(isfloat("1_2_3.4")) True 123.4, underscores ignored
print(isfloat("NaN")) True nan is also float
print(isfloat("123.456")) True
print(isfloat("123.E4")) True
print(isfloat(".1")) True
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("1,234")) False
print(isfloat("NULL")) False case insensitive
print(isfloat("NaNananana BATMAN")) False
print(isfloat(",1")) False
print(isfloat("123.EE4")) False
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
Sinking exceptions like this is bad, because killing canaries is bad because the float method can fail for reasons other than user input. Do not be using code like this on life critical software. Also python has been changing its contract on what unicode strings can be promoted to float so expect this behavior of this code to change on major version updates.
'1.43'.replace('.','',1).isdigit()
which will return true only if there is one or no '.' in the string of digits.
'1.4.3'.replace('.','',1).isdigit()
will return false
'1.ww'.replace('.','',1).isdigit()
will return false
TL;DR:
If your input is mostly strings that can be converted to floats, the try: except: method is the best native Python method.
If your input is mostly strings that cannot be converted to floats, regular expressions or the partition method will be better.
If you are 1) unsure of your input or need more speed and 2) don't mind and can install a third-party C-extension, fastnumbers works very well.
There is another method available via a third-party module called fastnumbers (disclosure, I am the author); it provides a function called isfloat. I have taken the unittest example outlined by Jacob Gabrielson in this answer, but added the fastnumbers.isfloat method. I should also note that Jacob's example did not do justice to the regex option because most of the time in that example was spent in global lookups because of the dot operator... I have modified that function to give a fairer comparison to 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()
On my machine, the output is:
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
As you can see, regex is actually not as bad as it originally seemed, and if you have a real need for speed, the fastnumbers method is quite good.
If you cared about performance (and I'm not suggesting you should), the try-based approach is the clear winner (compared with your partition-based approach or the regexp approach), as long as you don't expect a lot of invalid strings, in which case it's potentially slower (presumably due to the cost of exception handling).
Again, I'm not suggesting you care about performance, just giving you the data in case you're doing this 10 billion times a second, or something. Also, the partition-based code doesn't handle at least one valid string.
$ ./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)
Here's the code (Python 2.6, regexp taken from John Gietzen'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 __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()
Just for variety here is another method to do it.
>>> 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: Im sure it will not hold up to all cases of float though especially when there is an exponent. To solve that it looks like this. This will return True only val is a float and False for int but is probably less performant than 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
Simplified version of the function is_digit(str), which suffices in most cases (doesn't consider exponential notation and "NaN" value):
def is_digit(str):
return str.lstrip('-').replace('.', '').isdigit()
This regex will check for scientific floating point numbers:
^[-+]?(?:\b[0-9]+(?:\.[0-9]*)?|\.[0-9]+\b)(?:[eE][-+]?[0-9]+\b)?$
However, I believe that your best bet is to use the parser in a try.
If you don't need to worry about scientific or other expressions of numbers and are only working with strings that could be numbers with or without a period:
Function
def is_float(s):
result = False
if s.count(".") == 1:
if s.replace(".", "").isdigit():
result = True
return result
Lambda version
is_float = lambda x: x.replace('.','',1).isdigit() and "." in x
Example
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."
This way you aren't accidentally converting what should be an int, into a float.
I used the function already mentioned, but soon I notice that strings as "Nan", "Inf" and it's variation are considered as number. So I propose you improved version of the function, that will return false on those type of input and will not fail "1e3" variants:
def is_float(text):
# check for nan/infinity etc.
if text.isalpha():
return False
try:
float(text)
return True
except ValueError:
return False
You can use the try-except-else clause , this will catch any conversion/ value errors raised when the value passed cannot be converted to a float
def try_parse_float(item):
result = None
try:
float(item)
except:
pass
else:
result = float(item)
return result
a simple function that get you the type of number without try and except operation
def number_type(number):
if number.isdigit():
return int(number)
elif number.replace(".","").isdigit():
return float(number)
else:
return(type(number))
I was looking for some similar code, but it looks like using try/excepts is the best way.
Here is the code I'm using. It includes a retry function if the input is invalid. I needed to check if the input was greater than 0 and if so convert it to a 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: ")
Try to convert to float. If there is an error, print the ValueError exception.
try:
x = float('1.23')
print('val=',x)
y = float('abc')
print('val=',y)
except ValueError as err:
print('floatErr;',err)
Output:
val= 1.23
floatErr: could not convert string to float: 'abc'
Passing dictionary as argument it will convert strings which can be converted to float and will leave others
def covertDict_float(data):
for i in data:
if data[i].split(".")[0].isdigit():
try:
data[i] = float(data[i])
except:
continue
return data
I tried some of the above simple options, using a try test around converting to a float, and found that there is a problem in most of the replies.
Simple test (along the lines of above answers):
entry = ttk.Entry(self, validate='key')
entry['validatecommand'] = (entry.register(_test_num), '%P')
def _test_num(P):
try:
float(P)
return True
except ValueError:
return False
The problem comes when:
You enter '-' to start a negative number:
You are then trying float('-') which fails
You enter a number, but then try to delete all the digits
You are then trying float('') which likewise also fails
The quick solution I had is:
def _test_num(P):
if P == '' or P == '-': return True
try:
float(P)
return True
except ValueError:
return False
It seems many regex given miss one thing or another. This has been working for me so far:
(?i)^\s*[+-]?(?:inf(inity)?|nan|(?:\d+\.?\d*|\.\d+)(?:e[+-]?\d+)?)\s*$
It allows for infinity (or inf) with sign, nan, no digit before the
decimal, and leading/trailing spaces (if desired). The ^ and $ are
needed to keep from partially matching something like 1.2f-2 as 1.2.
You could use [ed] instead of just e if you need to parse some files
where D is used for double-precision scientific notation. You would
want to replace it afterward or just replace them before checking since
the float() function won't allow it.
I found a way that could also work. need to verify this. first time putting something here.
def isfloat(a_str):
try:
x=float(a_str)
if x%1 == 0:
return False
elif x%1 != 0: #an else also do
return True
except Exception as error:
return False
This works like a charm:
[dict([a,int(x) if isinstance(x, str)
and x.isnumeric() else float(x) if isinstance(x, str)
and x.replace('.', '', 1).isdigit() else x] for a, x in json_data.items())][0]
I've written my own functions. Instead of float(value), I use floatN() or floatZ(). which return None or 0.0 if the value can't be cast as a float. I keep them in a module I've called safeCasts.
def floatN(value):
try:
if value is not None:
fvalue = float(value)
else:
fvalue = None
except ValueError:
fvalue = None
return fvalue
def floatZ(value):
try:
if value is not None:
fvalue = float(value)
else:
fvalue = 0.0
except ValueError:
fvalue = 0.0
return fvalue
In other modules I import them
from safeCasts import floatN, floatZ
then use floatN(value) or floatZ(value) instead of float(). Obviously, you can use this technique for any cast function you need.
It's a simple, yet interesting question. Solution presented below works fine for me:
import re
val = "25,000.93$"
regex = r"\D"
splitted = re.split(regex, val)
splitted = list(filter(str.isdecimal, splitted))
if splitted:
if len(splitted) > 1:
splitted.insert(-1, ".")
try:
f = float("".join(splitted))
print(f, "is float.")
except ValueError:
print("Not a float.")
else:
print("Not a float.")
Important note: this solution is based on assumption that the last value in splitted contains decimal places.
You can create a function isfloat(), and use in place of isdigit() for both integers and floats, but not strings as you expect.
a = raw_input('How much is 1 share in that company? \n')
def isfloat(num):
try:
float(num)
return True
except:
return False
while not isfloat(a):
print("You need to write a number!\n")
a = raw_input('How much is 1 share in that company? \n')
We can use regex as:
import re
if re.match('[0-9]*.?[0-9]+', <your_string>):
print("Its a float/int")
else:
print("Its something alien")
let me explain the regex in english,
* -> 0 or more occurence
+ -> 1 or more occurence
? -> 0/1 occurence
now, lets convert
'[0-9]* -> let there be 0 or more occurence of digits in between 0-9
\.? -> followed by a 0 or one '.'(if you need to check if it can be int/float else we can also use instead of ?, use {1})
[0-9]+ -> followed by 0 or more occurence of digits in between 0-9
str(strval).isdigit()
seems to be simple.
Handles values stored in as a string or int or float
Related
Function that returns true or false if certain conditions are met?
So a user will input a string value in binary format. i.e. '01000001' I want to check the value they enter to see if it: has only eight characters. is a string type and only contains '0's or '1's preferably to be done with a function that takes in the user value so that i can call it whenever. Returns false if conditions are not met. this is what i came up with so far... size = len(teststring) teststring = '11111111' def typecheck(value): if type(user_input) == type(teststring) and len(user_input) == size and contains only 1 | 0 return
You can use regex matching here: reg = re.compile(r'^[01]{8}$') def typecheck(value): return isinstance(value, str) and bool(reg.match(value)) Or since you want to check for binary format number, how about converting it to int with base 2, and see if it's a valid conversion: def typecheck(value): try: return len(value) == 8 and bool(int(value, 2)) except TypeError, ValueError: return False
No need for regex You could use str.strip() and strip all 1 and 0 from end and beginning and check. Code: check = "11101110" if isinstance(check,str) and len(check)==8 and check.strip("01")=='': print "yes" Output: yes
This can be done with one if statement. If the regex doesn't find a match it will return None which evaluates to False in a truth test. import re def check_input(user_input): if not re.match(r'^[01]{8}$', user_input): it doesn't meet requirements do something
Python how to check if a string represent a float? [duplicate]
How do I check if a string represents a numeric value in Python? def is_number(s): try: float(s) return True except ValueError: return False The above works, but it seems clunky. If what you are testing comes from user input, it is still a string even if it represents an int or a float. See How can I read inputs as numbers? for converting the input, and Asking the user for input until they give a valid response for ensuring that the input represents an int or float (or other requirements) before proceeding.
For non-negative (unsigned) integers only, use isdigit(): >>> a = "03523" >>> a.isdigit() True >>> b = "963spam" >>> b.isdigit() False Documentation for isdigit(): Python2, Python3 For Python 2 Unicode strings: isnumeric().
Which, not only is ugly and slow I'd dispute both. A regex or other string parsing method would be uglier and slower. I'm not sure that anything much could be faster than the above. It calls the function and returns. Try/Catch doesn't introduce much overhead because the most common exception is caught without an extensive search of stack frames. The issue is that any numeric conversion function has two kinds of results A number, if the number is valid A status code (e.g., via errno) or exception to show that no valid number could be parsed. C (as an example) hacks around this a number of ways. Python lays it out clearly and explicitly. I think your code for doing this is perfect.
TL;DR The best solution is s.replace('.','',1).isdigit() I did some benchmarks comparing the different approaches def is_number_tryexcept(s): """ Returns True if string is a number. """ try: float(s) return True except ValueError: return False import re def is_number_regex(s): """ Returns True if 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 if string is a number. """ return s.replace('.','',1).isdigit() If the string is not a number, the except-block is quite slow. But more importantly, the try-except method is the only approach that handles scientific notations correctly. 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__) Float notation ".1234" is not supported by: 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) Scientific notation "1.000000e+50" is not supported by: is_number_regex is_number_repl_isdigit Scientific notation "1e50" is not supported by: is_number_regex is_number_repl_isdigit EDIT: The benchmark results 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))) where the following functions were tested from re import match as re_match from re import compile as re_compile def is_number_tryexcept(s): """ Returns True if string is a number. """ try: float(s) return True except ValueError: return False def is_number_regex(s): """ Returns True if 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 if string is a number. """ if comp.match(s) is None: return s.isdigit() return True def is_number_repl_isdigit(s): """ Returns True if string is a number. """ return s.replace('.','',1).isdigit()
There is one exception that you may want to take into account: the string 'NaN' If you want is_number to return FALSE for 'NaN' this code will not work as Python converts it to its representation of a number that is not a number (talk about identity issues): >>> float('NaN') nan Otherwise, I should actually thank you for the piece of code I now use extensively. :) G.
how about this: '3.14'.replace('.','',1).isdigit() which will return true only if there is one or no '.' in the string of digits. '3.14.5'.replace('.','',1).isdigit() will return false edit: just saw another comment ... adding a .replace(badstuff,'',maxnum_badstuff) for other cases can be done. if you are passing salt and not arbitrary condiments (ref:xkcd#974) this will do fine :P
Updated after Alfe pointed out you don't need to check for float separately as complex handles both: def is_number(s): try: complex(s) # for int, long, float and complex except ValueError: return False return True Previously said: Is some rare cases you might also need to check for complex numbers (e.g. 1+2i), which can not be represented by a float: 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
Which, not only is ugly and slow, seems clunky. It may take some getting used to, but this is the pythonic way of doing it. As has been already pointed out, the alternatives are worse. But there is one other advantage of doing things this way: polymorphism. The central idea behind duck typing is that "if it walks and talks like a duck, then it's a duck." What if you decide that you need to subclass string so that you can change how you determine if something can be converted into a float? Or what if you decide to test some other object entirely? You can do these things without having to change the above code. Other languages solve these problems by using interfaces. I'll save the analysis of which solution is better for another thread. The point, though, is that python is decidedly on the duck typing side of the equation, and you're probably going to have to get used to syntax like this if you plan on doing much programming in Python (but that doesn't mean you have to like it of course). One other thing you might want to take into consideration: Python is pretty fast in throwing and catching exceptions compared to a lot of other languages (30x faster than .Net for instance). Heck, the language itself even throws exceptions to communicate non-exceptional, normal program conditions (every time you use a for loop). Thus, I wouldn't worry too much about the performance aspects of this code until you notice a significant problem.
For int use this: >>> "1221323".isdigit() True But for float we need some tricks ;-). Every float number has one point... >>> "12.34".isdigit() False >>> "12.34".replace('.','',1).isdigit() True >>> "12.3.4".replace('.','',1).isdigit() False Also for negative numbers just add lstrip(): >>> '-12'.lstrip('-') '12' And now we get a universal way: >>> '-12.34'.lstrip('-').replace('.','',1).isdigit() True >>> '.-234'.lstrip('-').replace('.','',1).isdigit() False
This answer provides step by step guide having function with examples to find the string is: Positive integer Positive/negative - integer/float How to discard "NaN" (not a number) strings while checking for number? Check if string is positive integer You may use str.isdigit() to check whether given string is positive integer. Sample Results: # For digit >>> '1'.isdigit() True >>> '1'.isalpha() False Check for string as positive/negative - integer/float str.isdigit() returns False if the string is a negative number or a float number. For example: # returns `False` for float >>> '123.3'.isdigit() False # returns `False` for negative number >>> '-123'.isdigit() False If you want to also check for the negative integers and float, then you may write a custom function to check for it as: 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 Sample Run: >>> 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 Discard "NaN" (not a number) strings while checking for number The above functions will return True for the "NAN" (Not a number) string because for Python it is valid float representing it is not a number. For example: >>> is_number('NaN') True In order to check whether the number is "NaN", you may use math.isnan() as: >>> import math >>> nan_num = float('nan') >>> math.isnan(nan_num) True Or if you don't want to import additional library to check this, then you may simply check it via comparing it with itself using ==. Python returns False when nan float is compared with itself. For example: # `nan_num` variable is taken from above example >>> nan_num == nan_num False Hence, above function is_number can be updated to return False for "NaN" as: 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 Sample Run: >>> 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: Each operation for each check depending on the type of number comes with additional overhead. Choose the version of is_number function which fits your requirement.
For strings of non-numbers, try: except: is actually slower than regular expressions. For strings of valid numbers, regex is slower. So, the appropriate method depends on your input. If you find that you are in a performance bind, you can use a new third-party module called fastnumbers that provides a function called isfloat. Full disclosure, I am the author. I have included its results in the timings below. 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 As you can see try: except: was fast for numeric input but very slow for an invalid input regex is very efficient when the input is invalid fastnumbers wins in both cases
I know this is particularly old but I would add an answer I believe covers the information missing from the highest voted answer that could be very valuable to any who find this: For each of the following methods connect them with a count if you need any input to be accepted. (Assuming we are using vocal definitions of integers rather than 0-255, etc.) x.isdigit() works well for checking if x is an integer. x.replace('-','').isdigit() works well for checking if x is a negative.(Check - in first position) x.replace('.','').isdigit() works well for checking if x is a decimal. x.replace(':','').isdigit() works well for checking if x is a ratio. x.replace('/','',1).isdigit() works well for checking if x is a fraction.
Just Mimic C# In C# there are two different functions that handle parsing of scalar values: Float.Parse() Float.TryParse() float.parse(): def parse(string): try: return float(string) except Exception: throw TypeError Note: If you're wondering why I changed the exception to a TypeError, here's the documentation. float.try_parse(): def try_parse(string, fail=None): try: return float(string) except Exception: return fail; Note: You don't want to return the boolean 'False' because that's still a value type. None is better because it indicates failure. Of course, if you want something different you can change the fail parameter to whatever you want. To extend float to include the 'parse()' and 'try_parse()' you'll need to monkeypatch the 'float' class to add these methods. If you want respect pre-existing functions the code should be something like: def monkey_patch(): if(!hasattr(float, 'parse')): float.parse = parse if(!hasattr(float, 'try_parse')): float.try_parse = try_parse SideNote: I personally prefer to call it Monkey Punching because it feels like I'm abusing the language when I do this but YMMV. Usage: 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 And the great Sage Pythonas said to the Holy See Sharpisus, "Anything you can do I can do better; I can do anything better than you."
Casting to float and catching ValueError is probably the fastest way, since float() is specifically meant for just that. Anything else that requires string parsing (regex, etc) will likely be slower due to the fact that it's not tuned for this operation. My $0.02.
You can use Unicode strings, they have a method to do just what you want: >>> s = u"345" >>> s.isnumeric() True Or: >>> s = "345" >>> u = unicode(s) >>> u.isnumeric() True http://www.tutorialspoint.com/python/string_isnumeric.htm http://docs.python.org/2/howto/unicode.html
So to put it all together, checking for Nan, infinity and complex numbers (it would seem they are specified with j, not i, i.e. 1+2j) it results in: 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
I wanted to see which method is fastest. Overall the best and most consistent results were given by the check_replace function. The fastest results were given by the check_exception function, but only if there was no exception fired - meaning its code is the most efficient, but the overhead of throwing an exception is quite large. Please note that checking for a successful cast is the only method which is accurate, for example, this works with check_exception but the other two test functions will return False for a valid float: huge_number = float('1e+100') Here is the benchmark code: 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)) Here are the results with Python 2.7.10 on a 2017 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 Here are the results with Python 3.6.5 on a 2017 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 Here are the results with PyPy 2.7.13 on a 2017 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
The input may be as follows: a="50" b=50 c=50.1 d="50.1" 1-General input: The input of this function can be everything! Finds whether the given variable is numeric. Numeric strings consist of optional sign, any number of digits, optional decimal part and optional exponential part. Thus +0123.45e6 is a valid numeric value. Hexadecimal (e.g. 0xf4c3b00c) and binary (e.g. 0b10100111001) notation is not allowed. is_numeric function 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 test: >>> is_numeric("54") True >>> is_numeric("54.545") True >>> is_numeric("0x45") True is_float function Finds whether the given variable is float. float strings consist of optional sign, any number of digits, ... 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 test: >>> is_float("5.4") True >>> is_float("5") False >>> is_float(5) False >>> is_float("5") False >>> is_float("+5.4") True what is ast? 2- If you are confident that the variable content is String: use str.isdigit() method >>> 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-Numerical input: detect int value: >>> isinstance("54", int) False >>> isinstance(54, int) True >>> detect float: >>> isinstance("45.1", float) False >>> isinstance(45.1, float) True
In a most general case for a float, one would like to take care of integers and decimals. Let's take the string "1.1" as an example. I would try one of the following: 1.> isnumeric() word = "1.1" "".join(word.split(".")).isnumeric() >>> True 2.> isdigit() word = "1.1" "".join(word.split(".")).isdigit() >>> True 3.> isdecimal() word = "1.1" "".join(word.split(".")).isdecimal() >>> True Speed: ► All the aforementioned methods have similar speeds. %timeit "".join(word.split(".")).isnumeric() >>> 257 ns ± 12 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each) %timeit "".join(word.split(".")).isdigit() >>> 252 ns ± 11 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each) %timeit "".join(word.split(".")).isdecimal() >>> 244 ns ± 7.17 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
str.isnumeric() Return True if all characters in the string are numeric characters, and there is at least one character, False otherwise. Numeric characters include digit characters, and all characters that have the Unicode numeric value property, e.g. U+2155, VULGAR FRACTION ONE FIFTH. Formally, numeric characters are those with the property value Numeric_Type=Digit, Numeric_Type=Decimal or Numeric_Type=Numeric. str.isdecimal() Return True if all characters in the string are decimal characters and there is at least one character, False otherwise. Decimal characters are those that can be used to form numbers in base 10, e.g. U+0660, ARABIC-INDIC DIGIT ZERO. Formally a decimal character is a character in the Unicode General Category “Nd”. Both available for string types from Python 3.0.
I needed to determine if a string cast into basic types (float,int,str,bool). After not finding anything on the internet I created this: 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) Example 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 You can capture the type and use it s = "6.0" type_ = str_to_type(s) # float f = type_(s)
I think your solution is fine, but there is a correct regexp implementation. There does seem to be a lot of regexp hate towards these answers which I think is unjustified, regexps can be reasonably clean and correct and fast. It really depends on what you're trying to do. The original question was how can you "check if a string can be represented as a number (float)" (as per your title). Presumably you would want to use the numeric/float value once you've checked that it's valid, in which case your try/except makes a lot of sense. But if, for some reason, you just want to validate that a string is a number then a regex also works fine, but it's hard to get correct. I think most of the regex answers so far, for example, do not properly parse strings without an integer part (such as ".7") which is a float as far as python is concerned. And that's slightly tricky to check for in a single regex where the fractional portion is not required. I've included two regex to show this. It does raise the interesting question as to what a "number" is. Do you include "inf" which is valid as a float in python? Or do you include numbers that are "numbers" but maybe can't be represented in python (such as numbers that are larger than the float max). There's also ambiguities in how you parse numbers. For example, what about "--20"? Is this a "number"? Is this a legal way to represent "20"? Python will let you do "var = --20" and set it to 20 (though really this is because it treats it as an expression), but float("--20") does not work. Anyways, without more info, here's a regex that I believe covers all the ints and floats as python parses them. # 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 Some example test values: 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 Running the benchmarking code in #ron-reiter's answer shows that this regex is actually faster than the normal regex and is much faster at handling bad values than the exception, which makes some sense. Results: 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
I did some speed test. Lets say that if the string is likely to be a number the try/except strategy is the fastest possible.If the string is not likely to be a number and you are interested in Integer check, it worths to do some test (isdigit plus heading '-'). If you are interested to check float number, you have to use the try/except code whitout escape.
RyanN suggests If you want to return False for a NaN and Inf, change line to x = float(s); return (x == x) and (x - 1 != x). This should return True for all floats except Inf and NaN But this doesn't quite work, because for sufficiently large floats, x-1 == x returns true. For example, 2.0**54 - 1 == 2.0**54
I was working on a problem that led me to this thread, namely how to convert a collection of data to strings and numbers in the most intuitive way. I realized after reading the original code that what I needed was different in two ways: 1 - I wanted an integer result if the string represented an integer 2 - I wanted a number or a string result to stick into a data structure so I adapted the original code to produce this derivative: def string_or_number(s): try: z = int(s) return z except ValueError: try: z = float(s) return z except ValueError: return s
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
This code handles the exponents, floats, and integers, wihtout using regex. return True if str1.lstrip('-').replace('.','',1).isdigit() or float(str1) else False
Here's my simple way of doing it. Let's say that I'm looping through some strings and I want to add them to an array if they turn out to be numbers. try: myvar.append( float(string_to_check) ) except: continue Replace the myvar.apppend with whatever operation you want to do with the string if it turns out to be a number. The idea is to try to use a float() operation and use the returned error to determine whether or not the string is a number.
I also used the function you mentioned, but soon I notice that strings as "Nan", "Inf" and it's variation are considered as number. So I propose you improved version of your function, that will return false on those type of input and will not fail "1e3" variants: def is_float(text): try: float(text) # check for nan/infinity etc. if text.isalpha(): return False return True except ValueError: return False
User helper function: def if_ok(fn, string): try: return fn(string) except Exception as e: return None then 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)])
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
Pythonic way to handle validation
I am writing a small function that takes an integer value and a string such as: param1: 1 param2: "1 1 1" The function will split the string parameter and validate its len against the first parameter like so: def prepare_integer_set(expected_len, integer_string): prepared_integer_set = integer_string.split() if len(prepared_integer_set) != expected_len: raise ValueError('Number of expected integers does not match integer string') return [int(x) for x in prepared_integer_set] However, the exception is bothering me. I would rather return false as the input comes from the user so is not strictly an exception if they make an error. How should this be handled? Split the method in 2, having a separate validation and preparation methods? Or is it pythonic as it currently is by throwing the exception? Here would be the alternative which is split: def validate_integer_set(expected_len, integer_set): return expected_len == len(integer_set) def prepare_integer_set(integer_string): prepared_integer_set = integer_string.split() return [int(x) for x in prepared_integer_set]
You need to decide what prepare_integer_set() is doing: If it is just validating user input, it should return True or False. If it returns True, you then process the data as normal. If it is doing something with the data, then invalid data ought to result in an exception. You could alternatively return None or some other falsey value, but be careful. If your normal return value could also be falsey (e.g. an empty list), then this is likely to cause more trouble than it's worth.
I would return False if you don't want to raise an error then use a simple if check later before proceeding: def prepare_integer_set(expected_len, integer_string): prepared_integer_set = integer_string.split() # catch 0 and negative input if len(prepared_integer_set) != expected_len or expected_len < 1: return False return [int(x) for x in prepared_integer_set] val = prepare_integer_set(2,"1 2") if val: # if length test failed it will evaluate to False else we have a list of ints which will evaluate to True ... You also have to consider input like, prepare_integer_set(3,"1 2 foo") that will pass your current length test but fail when casting. So you may want to use a try/except returning False again. def prepare_integer_set(expected_len, integer_string): prepared_integer_set = integer_string.split() if len(prepared_integer_set) != expected_len or expected_len < 1: return False try: return [int(x) for x in prepared_integer_set] except ValueError: return False If you don't consider the wrong length to be an error then input that cannot be cast to an int should probably be treated the same or both should be considered an error and an exception raised instead of returning False. Really what you are doing with the value returned should dictate whether you should raise an error or return False for bad input, I don't think doing different things for both situations is a good idea.
Is there a more Pythonic way to test if an object is a number or return its value? [duplicate]
This question already has answers here: How to convert a string to a number if it has commas in it as thousands separators? (10 answers) Closed 9 years ago. I have not tried decorators yet or functions within functions, but this current un-pythonic method seems a little convoluted. I dont like how I need to repeat myself when I check the return_type (3 times)? Any suggestions are welcome, especially if the repetion can be dealt with in an elegant way. Please note that I am not that interested in the numerous reference to test if an object is a number as I believe this part is incorporated within my solution. I am interested in addressing the 3x duplication to deal with the return type. Moreover, while I appreciate the locale method is the more rigorous way of dealing with internationalisation, I prefer the simplicity of allowing the caller more flexibility in choosing the characters. Thanks def is_number(obj, thousand_sep=',', decimal_sep=None, return_type='b'): """ determines if obj is numeric. if return_type = b, returns a boolean True/False otherwise, it returns the numeric value Examples -------- >>> is_number(3) True >>> is_number('-4.1728') True >>> is_number('-4.1728', return_type='n') -4.1728 >>> is_number(-5.43) True >>> is_number("20,000.43") True >>> is_number("20.000,43", decimal_sep=",", thousand_sep=",") True >>> is_number("20.000,43", decimal_sep=",", thousand_sep=".", return_type="n") 20000.43 >>> is_number('Four') False # I am a few light years away from working that one out!!! """ try: if is_string(obj): if decimal_sep is None: value = float(obj.replace(thousand_sep, "")) else: value = float(obj.replace(thousand_sep, "").replace(decimal_sep, ".")) if return_type.lower() == 'b': return True else: return value else: value = float(obj) if return_type.lower() == 'b': return True else: return value except ValueError: return False if return_type.lower() == 'b': return False else: return None
using regular expressions, you may do: import re regex = re.compile( r'[+-]{0,1}\d{1,3}(,\d\d\d)*(\.\d+)*' now if you have string txt, and do below regex.sub( '', txt, count=1 ) you will end up with an empty string if that string is a number with , as thousands separator and . as decimal separator. This method enforces a strict 3 digits thousands separator. so for example 20,0001.43 is not a number because the thousands separator is wrong. 1220,001.43 is not a number either because it is missing a ,. def numval( txt ): import re regex = re.compile( r'[+-]{0,1}\d{1,3}(,\d\d\d)*(\.\d+)*' if regex.sub( '', txt.strip(' '), count=1 ) != '': raise ValueError ( 'not a number' ) else: return float( txt.replace( ',', '' ))
I would probably separate the logic ... I think this does what you are trying to do ... def get_non_base_10(s): #support for base 2,8,and 16 if s.startswith("O") and s[1:].isdigit(): return int(s[1:],8) elif s.startswith("0x") and s[2:].isdigit(): return int(s[2:],16) elif s.startswith("0b") and s[2:].isdigit(): return int(s[2:],2) def get_number(s,decimal_separator=".",thousands_separator=","): if isinstance(s,basestring): temp_val = get_non_base_10(s) if temp_val is not None: return temp_val s = s.replace(decimal_separator,".").replace(thousands_separator,"") try: return float(s) except ValueError: return "nan" def is_number(s,decimal_separator=".",thousands_separator=",",return_type="b"): numeric = get_number(s,decimal_separator,thousands_separator) return numeric if return_type != "b" else numeric != "nan"
IndexError string index out of range
s="(8+(2+4))" def checker(n): if len(n) == 0: return True if n[0].isdigit==True: if n[1].isdigit==True: return False else: checker(n[1:]) else: checker(n[1:]) This is what I have so far. Simple code, trying to see if a string meets the following conditions. However when i perform checker(s) i get: True IndexError: string index out of range Any help? Thanks in advance Edit: The function's purpose is to produce true if the string contains only single digit numbers, and false if 2 or more-figured digits exist in the string.
When the length of n is 0, the n[0] part is going to raise an error because the string in empty. You should add a return statement there instead of print. def checker(n): if len(n) < 2: return True if n[0] in x: Note that the conditions must be len(n) < 2 otherwise you'll get an error on n[1] when the length of string is 1. Secondly you're trying to match characters to a list which contains integers, so the in checks are always going to be False. Either convert the list items to string or better use str.isdigit. >>> '1'.isdigit() True >>> ')'.isdigit() False >>> '12'.isdigit() True Update: You can use regex and all for this: >>> import re def check(strs): nums = re.findall(r'\d+',strs) return all(len(c) == 1 for c in nums) ... >>> s="(8+(2+4))" >>> check(s) True >>> check("(8+(2+42))") False Working version of your code: s="(8+(2+4))" def checker(n): if not n: #better than len(n) == 0, empty string returns False in python return True if n[0].isdigit(): #str.digit is a method and it already returns a boolean value if n[1].isdigit(): return False else: return checker(n[1:]) # use return statement for recursive calls # otherwise the recursive calls may return None else: return checker(n[1:]) print checker("(8+(2+4))") print checker("(8+(2+42))") output: True False
You should do return True after the first if statement, not print True. The function continues to run after that statement and hits an error when the input is size 0.
I can't reproduce your error. I had to fix a few things: Indentation, which I'm guessing was just a problem pasting into the page .isdigit() is a function; calling .isdigit==True is going to compare a function object with True, which is never going to be true. I changed .isdigit==True to .isdigit() I made sure return value gets bubbled up - without this, the recursion completes okay but the outermost function just returns "None". Aside from that, a few print statements show that this is working as expected s="(8+(2+4))" t="(8+(20+4))" def checker(n): print "checking %s" % n if len(n) == 0: print "Returning true" return True if n[0].isdigit(): if n[1].isdigit(): print "returning false" return False else: return checker(n[1:]) else: return checker(n[1:]) print checker(s) print checker(t) Output: checking (8+(2+4)) checking 8+(2+4)) checking +(2+4)) checking (2+4)) checking 2+4)) checking +4)) checking 4)) checking )) checking ) checking Returning true True checking (8+(20+4)) checking 8+(20+4)) checking +(20+4)) checking (20+4)) checking 20+4)) returning false False