问题
What is the most "correct", Pythonic way to do user input validation in Python?
I've been using the following:
while True:
stuff = input("Please enter foo: ")
try:
some_test(stuff)
print("Thanks.")
break
except SomeException:
print("Invalid input.")
Which is nice and readable, I suppose, but I can't help wondering if there isn't some built-in function or something I should be using instead.
回答1:
I like decorators to separate the checking from the rest of the input handling.
#!/usr/bin/env python
def repeatOnError(*exceptions):
def checking(function):
def checked(*args, **kwargs):
while True:
try:
result = function(*args, **kwargs)
except exceptions as problem:
print "There was a problem with the input:"
print problem.__class__.__name__
print problem
print "Please repeat!"
else:
return result
return checked
return checking
@repeatOnError(ValueError)
def getNumberOfIterations():
return int(raw_input("Please enter the number of iterations: "))
iterationCounter = getNumberOfIterations()
print "You have chosen", iterationCounter, "iterations."
EDIT:
A decorator is more or less a wrapper for an existing function (or method). It takes the existing function (denoted below its @decorator
directive) and returns a "replacement" for it. This replacement in our case calls the original function in a loop and catches any exception happening while doing so. If no exception happens, it just returns the result of the original function.
回答2:
The most Pythonic way to do this kind of validation of "User INput" is to catch an appropriate exception.
Example:
def get_user_input():
while True:
try:
return int(input("Please enter a number: "))
except ValueError:
print("Invalid input. Please try again!")
n = get_user_input()
print("Thanks! You entered: {0:d}".format(n))
It's also good practice to allow exceptions occur where they lie and allow them to bubble up rather than hide them so you can clearly see what's going wrong in a Python Traceback.
In this case Validating User Input -- Use Python's Duck Typing and catch the error. i.e: If it acts like a duct, it must be a duck. (If it acts like an int, it must be an int).
回答3:
A bit complicated, but may be interesting:
import re
from sys import exc_info,excepthook
from traceback import format_exc
def condition1(stuff):
'''
stuff must be the string of an integer'''
try:
i = int(stuff)
return True
except:
return False
def condition2(stuff):
'''
stuff is the string of an integer
but the integer must be in the range(10,30)'''
return int(stuff) in xrange(10,30)
regx = re.compile('assert *\( *([_a-z\d]+)')
while True:
try:
stuff = raw_input("Please enter foo: ")
assert(condition1(stuff))
assert ( condition2(stuff))
print("Thanks.")
break
except AssertionError:
tbs = format_exc(exc_info()[0])
funky = globals()[regx.search(tbs).group(1)]
excepthook(exc_info()[0], funky.func_doc, None)
result
Please enter foo: g
AssertionError:
stuff must be the string of an integer
Please enter foo: 170
AssertionError:
stuff is the string of an integer
but the integer must be in the range(10,30)
Please enter foo: 15
Thanks.
.
EDIT
I found a way to simplify:
from sys import excepthook
def condition1(stuff):
'''
stuff must be the string of an integer'''
try:
int(stuff)
return True
except:
return False
def another2(stuff):
'''
stuff is the string of an integer
but the integer must be in the range(10,30)'''
return int(stuff) in xrange(10,30)
tup = (condition1,another2)
while True:
try:
stuff = raw_input("Please enter foo: ")
for condition in tup:
assert(condition(stuff))
print("Thanks.")
break
except AssertionError:
excepthook('AssertionError', condition.func_doc, None)
来源:https://stackoverflow.com/questions/20540274/most-pythonic-way-to-do-input-validation