I am using Python 3.1.2
What is the best way to get numeric (float or int) user input?
In Python 2, the input() builtin function was simply
You should do something like
In Python3 input() will give you string. You can use eval(input()) to get numbers, but I would make sure that the string you are using with eval() does not contain any nasty commands.
Here is a function that will check for valid numeric input ...
# a simple input function to guarantee numeric input # tested with Python 3.1.2 def input_num(prompt="Enter a number: "): """ prompt the user for a numeric input prompt again if the input is not numeric return an integer or a float """ while True: # strip() removes any leading or trailing whitespace num_str = input(prompt).strip() num_flag = True for c in num_str: # check for non-numerics if c not in '+-.0123456789': num_flag = False if num_flag: break # a float contains a period (US) if '.' in num_str: return float(num_str) else: return int(num_str) def input_num2(prompt="Enter a number: "): """ prompt the user for a numeric input prompt again if the input is not numeric return an integer or a float """ while True: # strip() removes any leading or trailing whitespace num_str = input(prompt).strip() # make sure that all char can be in a typical number if all(c in '+-.0123456789' for c in num_str): break # a float contains a period (US) if '.' in num_str: return float(num_str) else: return int(num_str) # test ... num = input_num() print(type(num)) print(num) print('-'*20) num2 = input_num2() print(type(num2)) print(num2)
Function input_num2() is mildly more pythonic.
Edited by vegaseat: input_num2()
Nice piece of work, especially
all(c in '+-.0123456789' for c in num_str) if you can trust the correctness of input. Has some problems though:
>>> Enter a number: 1.234.567 Traceback (most recent call last): File "D:/Tony/input_num.py", line 45, in <module> num = input_num() File "D:/Tony/input_num.py", line 12, in input_num num_str = input(prompt).strip() File "<string>", line 1 1.234.567 ^ SyntaxError: unexpected EOF while parsing >>>
If you really wait really correct input you can put everything in while True loop which has try... except and break out of loop. Or you can revive the algorithm so that
a) read whitespace
b) read sign or number
c) continue reading number drop '0' until read number in '123456789' or decimal point (set flag if read point, if one number read)
d) do not accept if no number given (for example '-.')
Also you can use my code snippet for date reading and make condition to accept correct separators (even you could add handling for space or , for thousands separator) and same time drop white space/thousands separators for evaluation number. Basically drop white space as always OK and, then compare that list's separators are in '+-.', in order of the string (no 01.90001.234 accepted)
Edited by pyTony: n/a
Idiot proving is tough, since new idiots are coming out all the time!
Those idiots make ideal subjects for usability testing, though. This kind of input function defining is actualy small computer language definition, definition of what makes a number valid. Unnecessary complicated for me. Any pythonic parser module available?
Edited by pyTony: n/a
Here some first version of number testing without support yet for numbers in 1e7 scientific format.
This is unfortunately Python 2.6 code, probably runs also in python 3, not tested.
Routine strips whitespace and zeroes before numbers and puts '0' for whole part for numbers like .23423 -> 0.23423 and takes out trailing zeroes in fraction part.
## scanum checks that number is ok, does not yet recognize enginering format (e.g. 1e7) import random def checkint(i): s=[n for n in i if n.isdigit()] return ''.join(s)==i def scanum(x): x=x.lstrip() if x=='-': x=x[1:].lstrip('0') intpart,found,fracpart=x.partition('.') intpart=intpart or '0' ## use 0.234 instead of .234 ok=checkint(intpart) if found: ok = ok and checkint(fracpart) if not ok: return -1 #raise ValueError,'Improper whole or fraction part' if found: return '-'+intpart+'.'+fracpart.rstrip('0') else: return '-'+intpart else: if x=='+': x=x[1:] else: x=x.lstrip() x=x.lstrip('0') intpart,found,fracpart=x.partition('.') intpart=intpart or '0' ## use 0.234 instead of .234 ok=checkint(intpart) if found: ok = ok and checkint(fracpart) if not ok: return -1 ##raise ValueError,'Improper whole or fraction part' if found: return intpart+'.'+fracpart.rstrip('0') else: return intpart ## do some random testing for x in range(1000): i=''.join([x and i for x in range(1,10) for i in random.choice('-+1234567890.')]) i=scanum(i) if i != -1: print(i)
Edited by pyTony: print brackets
How about this.
#Python 3 try: input_num = float(input('test: ')) except ValueError: print ('Not an integer') print (input_num)
Edited by snippsat: n/a
Ok, but maybe, better to make error message more like
print("Not an valid number") and even tell in which number the problem was.
In my code there is possibility to add support for thousands separator and exchangeable dot/comma for desimal point (so you can accept both from user, for example numeric keypad has not point but comma here in Finland).
I did mention that format errors from earlier code still needed to be catched for user comfortable error handling (For example 1.000.000,00)
Basically only checking for correctness should be lighter job than changing from string to float and possibly back to string if that is needed, but as that is probably optimized in C, speed can be faster in this simple solution.
Main point is not to use eval but float.
One point is that it changes all numbers to float, and I do not know how it reacts to long integers (loosing accuracy).
float("123456")=123456.0 and type is float, not integer. I however think that in most cases this simplest way with try..except is good enough.
Edited by pyTony: n/a