Python 要求用户输入直到他们给出有效的响应
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/23294658/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Asking the user for input until they give a valid response
提问by Kevin
I am writing a program that accepts an input from the user.
我正在编写一个接受用户输入的程序。
#note: Python 2.7 users should use `raw_input`, the equivalent of 3.X's `input`
age = int(input("Please enter your age: "))
if age >= 18:
print("You are able to vote in the United States!")
else:
print("You are not able to vote in the United States.")
The program works as expected as long as the the user enters meaningful data.
只要用户输入有意义的数据,程序就会按预期工作。
C:\Python\Projects> canyouvote.py
Please enter your age: 23
You are able to vote in the United States!
But it fails if the user enters invalid data:
但如果用户输入无效数据,它就会失败:
C:\Python\Projects> canyouvote.py
Please enter your age: dickety six
Traceback (most recent call last):
File "canyouvote.py", line 1, in <module>
age = int(input("Please enter your age: "))
ValueError: invalid literal for int() with base 10: 'dickety six'
Instead of crashing, I would like the program to ask for the input again. Like this:
而不是崩溃,我希望程序再次要求输入。像这样:
C:\Python\Projects> canyouvote.py
Please enter your age: dickety six
Sorry, I didn't understand that.
Please enter your age: 26
You are able to vote in the United States!
How can I make the program ask for valid inputs instead of crashing when non-sensical data is entered?
当输入无意义的数据时,如何让程序要求有效输入而不是崩溃?
How can I reject values like -1
, which is a valid int
, but nonsensical in this context?
我如何拒绝像 那样的值-1
,int
在这种情况下这是一个有效但无意义的值?
采纳答案by Kevin
The simplest way to accomplish this is to put the input
method in a while loop. Use continue
when you get bad input, and break
out of the loop when you're satisfied.
完成此操作的最简单方法是将input
方法置于 while 循环中。使用continue
时,你会得到错误的输入,并break
退出循环,当你满足。
When Your Input Might Raise an Exception
当您的输入可能引发异常时
Use try
and except
to detect when the user enters data that can't be parsed.
使用try
和except
检测用户何时输入无法解析的数据。
while True:
try:
# Note: Python 2.x users should use raw_input, the equivalent of 3.x's input
age = int(input("Please enter your age: "))
except ValueError:
print("Sorry, I didn't understand that.")
#better try again... Return to the start of the loop
continue
else:
#age was successfully parsed!
#we're ready to exit the loop.
break
if age >= 18:
print("You are able to vote in the United States!")
else:
print("You are not able to vote in the United States.")
Implementing Your Own Validation Rules
实现你自己的验证规则
If you want to reject values that Python can successfully parse, you can add your own validation logic.
如果要拒绝 Python 可以成功解析的值,可以添加自己的验证逻辑。
while True:
data = input("Please enter a loud message (must be all caps): ")
if not data.isupper():
print("Sorry, your response was not loud enough.")
continue
else:
#we're happy with the value given.
#we're ready to exit the loop.
break
while True:
data = input("Pick an answer from A to D:")
if data.lower() not in ('a', 'b', 'c', 'd'):
print("Not an appropriate choice.")
else:
break
Combining Exception Handling and Custom Validation
结合异常处理和自定义验证
Both of the above techniques can be combined into one loop.
以上两种技术都可以组合成一个循环。
while True:
try:
age = int(input("Please enter your age: "))
except ValueError:
print("Sorry, I didn't understand that.")
continue
if age < 0:
print("Sorry, your response must not be negative.")
continue
else:
#age was successfully parsed, and we're happy with its value.
#we're ready to exit the loop.
break
if age >= 18:
print("You are able to vote in the United States!")
else:
print("You are not able to vote in the United States.")
Encapsulating it All in a Function
将其全部封装在一个函数中
If you need to ask your user for a lot of different values, it might be useful to put this code in a function, so you don't have to retype it every time.
如果您需要向您的用户询问许多不同的值,将这些代码放在一个函数中可能会很有用,这样您就不必每次都重新键入它。
def get_non_negative_int(prompt):
while True:
try:
value = int(input(prompt))
except ValueError:
print("Sorry, I didn't understand that.")
continue
if value < 0:
print("Sorry, your response must not be negative.")
continue
else:
break
return value
age = get_non_negative_int("Please enter your age: ")
kids = get_non_negative_int("Please enter the number of children you have: ")
salary = get_non_negative_int("Please enter your yearly earnings, in dollars: ")
Putting It All Together
把它放在一起
You can extend this idea to make a very generic input function:
你可以扩展这个想法来制作一个非常通用的输入函数:
def sanitised_input(prompt, type_=None, min_=None, max_=None, range_=None):
if min_ is not None and max_ is not None and max_ < min_:
raise ValueError("min_ must be less than or equal to max_.")
while True:
ui = input(prompt)
if type_ is not None:
try:
ui = type_(ui)
except ValueError:
print("Input type must be {0}.".format(type_.__name__))
continue
if max_ is not None and ui > max_:
print("Input must be less than or equal to {0}.".format(max_))
elif min_ is not None and ui < min_:
print("Input must be greater than or equal to {0}.".format(min_))
elif range_ is not None and ui not in range_:
if isinstance(range_, range):
template = "Input must be between {0.start} and {0.stop}."
print(template.format(range_))
else:
template = "Input must be {0}."
if len(range_) == 1:
print(template.format(*range_))
else:
print(template.format(" or ".join((", ".join(map(str,
range_[:-1])),
str(range_[-1])))))
else:
return ui
With usage such as:
使用例如:
age = sanitised_input("Enter your age: ", int, 1, 101)
answer = sanitised_input("Enter your answer: ", str.lower, range_=('a', 'b', 'c', 'd'))
Common Pitfalls, and Why you Should Avoid Them
常见的陷阱,以及为什么要避免它们
The Redundant Use of Redundant input
Statements
冗余input
语句的冗余使用
This method works but is generally considered poor style:
这种方法有效,但通常被认为是糟糕的风格:
data = input("Please enter a loud message (must be all caps): ")
while not data.isupper():
print("Sorry, your response was not loud enough.")
data = input("Please enter a loud message (must be all caps): ")
It might look attractive initially because it's shorter than the while True
method, but it violates the Don't Repeat Yourselfprinciple of software development. This increases the likelihood of bugs in your system. What if you want to backport to 2.7 by changing input
to raw_input
, but accidentally change only the first input
above? It's a SyntaxError
just waiting to happen.
它最初可能看起来很有吸引力,因为它比while True
方法短,但它违反了软件开发的不要重复自己的原则。这会增加系统中出现错误的可能性。如果你想向移植到2.7通过改变input
来raw_input
,却意外地只改变第一input
上面?这SyntaxError
只是等待发生。
Recursion Will Blow Your Stack
递归会炸毁你的堆栈
If you've just learned about recursion, you might be tempted to use it in get_non_negative_int
so you can dispose of the while loop.
如果您刚刚了解了递归,您可能会想使用它get_non_negative_int
来处理 while 循环。
def get_non_negative_int(prompt):
try:
value = int(input(prompt))
except ValueError:
print("Sorry, I didn't understand that.")
return get_non_negative_int(prompt)
if value < 0:
print("Sorry, your response must not be negative.")
return get_non_negative_int(prompt)
else:
return value
This appears to work fine most of the time, but if the user enters invalid data enough times, the script will terminate with a RuntimeError: maximum recursion depth exceeded
. You may think "no fool would make 1000 mistakes in a row", but you're underestimating the ingenuity of fools!
这在大多数情况下似乎工作正常,但如果用户输入无效数据的次数足够多,脚本将以RuntimeError: maximum recursion depth exceeded
. 你可能认为“没有傻瓜会连续犯1000次错误”,但你低估了傻瓜的聪明才智!
回答by aaveg
Though the accepted answer is amazing. I would also like to share a quick hack for this problem. (This takes care of the negative age problem as well.)
虽然接受的答案是惊人的。我还想分享一个快速解决这个问题的方法。(这也解决了负年龄问题。)
f=lambda age: (age.isdigit() and ((int(age)>=18 and "Can vote" ) or "Cannot vote")) or \
f(input("invalid input. Try again\nPlease enter your age: "))
print(f(input("Please enter your age: ")))
P.S. This code is for python 3.x.
PS 此代码适用于 python 3.x。
回答by Steven Stip
Why would you do a while True
and then break out of this loop while you can also just put your requirements in the while statement since all you want is to stop once you have the age?
为什么要先执行 awhile True
然后跳出这个循环,而您也可以将您的要求放在 while 语句中,因为您只想在达到年龄后停止?
age = None
while age is None:
input_value = input("Please enter your age: ")
try:
# try and convert the string input to a number
age = int(input_value)
except ValueError:
# tell the user off
print("{input} is not a number, please enter a number only".format(input=input_value))
if age >= 18:
print("You are able to vote in the United States!")
else:
print("You are not able to vote in the United States.")
This would result in the following:
这将导致以下结果:
Please enter your age: *potato*
potato is not a number, please enter a number only
Please enter your age: *5*
You are not able to vote in the United States.
this will work since age will never have a value that will not make sense and the code follows the logic of your "business process"
这将起作用,因为 age 永远不会有一个没有意义的值,并且代码遵循您的“业务流程”的逻辑
回答by cat
So, I was messing around with something similar to this recently, and I came up with the following solution, which uses a way of getting input that rejects junk, before it's even checked in any logical way.
所以,我最近在搞类似的事情,我想出了以下解决方案,它使用一种拒绝垃圾的输入方式,甚至在以任何合乎逻辑的方式检查之前。
read_single_keypress()
courtesy https://stackoverflow.com/a/6599441/4532996
read_single_keypress()
礼貌https://stackoverflow.com/a/6599441/4532996
def read_single_keypress() -> str:
"""Waits for a single keypress on stdin.
-- from :: https://stackoverflow.com/a/6599441/4532996
"""
import termios, fcntl, sys, os
fd = sys.stdin.fileno()
# save old state
flags_save = fcntl.fcntl(fd, fcntl.F_GETFL)
attrs_save = termios.tcgetattr(fd)
# make raw - the way to do this comes from the termios(3) man page.
attrs = list(attrs_save) # copy the stored version to update
# iflag
attrs[0] &= ~(termios.IGNBRK | termios.BRKINT | termios.PARMRK
| termios.ISTRIP | termios.INLCR | termios. IGNCR
| termios.ICRNL | termios.IXON )
# oflag
attrs[1] &= ~termios.OPOST
# cflag
attrs[2] &= ~(termios.CSIZE | termios. PARENB)
attrs[2] |= termios.CS8
# lflag
attrs[3] &= ~(termios.ECHONL | termios.ECHO | termios.ICANON
| termios.ISIG | termios.IEXTEN)
termios.tcsetattr(fd, termios.TCSANOW, attrs)
# turn off non-blocking
fcntl.fcntl(fd, fcntl.F_SETFL, flags_save & ~os.O_NONBLOCK)
# read a single keystroke
try:
ret = sys.stdin.read(1) # returns a single character
except KeyboardInterrupt:
ret = 0
finally:
# restore old state
termios.tcsetattr(fd, termios.TCSAFLUSH, attrs_save)
fcntl.fcntl(fd, fcntl.F_SETFL, flags_save)
return ret
def until_not_multi(chars) -> str:
"""read stdin until !(chars)"""
import sys
chars = list(chars)
y = ""
sys.stdout.flush()
while True:
i = read_single_keypress()
_ = sys.stdout.write(i)
sys.stdout.flush()
if i not in chars:
break
y += i
return y
def _can_you_vote() -> str:
"""a practical example:
test if a user can vote based purely on keypresses"""
print("can you vote? age : ", end="")
x = int("0" + until_not_multi("0123456789"))
if not x:
print("\nsorry, age can only consist of digits.")
return
print("your age is", x, "\nYou can vote!" if x >= 18 else "Sorry! you can't vote")
_can_you_vote()
You can find the complete module here.
您可以在此处找到完整的模块。
Example:
例子:
$ ./input_constrain.py
can you vote? age : a
sorry, age can only consist of digits.
$ ./input_constrain.py
can you vote? age : 23<RETURN>
your age is 23
You can vote!
$ _
Note that the nature of this implementation is it closes stdin as soon as something that isn't a digit is read. I didn't hit enter after a
, but I needed to after the numbers.
请注意,此实现的性质是,一旦读取了不是数字的内容,它就会关闭 stdin。我没有按 Enter 之后a
,但我需要在数字之后。
You could merge this with the thismany()
function in the same module to only allow, say, three digits.
您可以将其与thismany()
同一模块中的函数合并,以只允许使用三位数字。
回答by 2Cubed
While a try
/except
block will work, a much faster and cleaner way to accomplish this task would be to use str.isdigit()
.
虽然try
/except
块可以工作,但完成此任务的更快、更简洁的方法是使用str.isdigit()
.
while True:
age = input("Please enter your age: ")
if age.isdigit():
age = int(age)
break
else:
print("Invalid number '{age}'. Try again.".format(age=age))
if age >= 18:
print("You are able to vote in the United States!")
else:
print("You are not able to vote in the United States.")
回答by ojas mohril
def validate_age(age):
if age >=0 :
return True
return False
while True:
try:
age = int(raw_input("Please enter your age:"))
if validate_age(age): break
except ValueError:
print "Error: Invalid age."
回答by Mangu Singh Rajpurohit
You can write more general logic to allow user to enter only specific number of times, as the same use-case arises in many real-world applications.
您可以编写更通用的逻辑以允许用户仅输入特定次数,因为在许多实际应用程序中都会出现相同的用例。
def getValidInt(iMaxAttemps = None):
iCount = 0
while True:
# exit when maximum attempt limit has expired
if iCount != None and iCount > iMaxAttemps:
return 0 # return as default value
i = raw_input("Enter no")
try:
i = int(i)
except ValueError as e:
print "Enter valid int value"
else:
break
return i
age = getValidInt()
# do whatever you want to do.
回答by Pratik Anand
Try this one:-
试试这个:-
def takeInput(required):
print 'ooo or OOO to exit'
ans = raw_input('Enter: ')
if not ans:
print "You entered nothing...!"
return takeInput(required)
## FOR Exit ##
elif ans in ['ooo', 'OOO']:
print "Closing instance."
exit()
else:
if ans.isdigit():
current = 'int'
elif set('[~!@#$%^&*()_+{}":/\']+$').intersection(ans):
current = 'other'
elif isinstance(ans,basestring):
current = 'str'
else:
current = 'none'
if required == current :
return ans
else:
return takeInput(required)
## pass the value in which type you want [str/int/special character(as other )]
print "input: ", takeInput('str')
回答by Saeed Zahedian Abroodi
Use "while" statement till user enter a true value and if the input value is not a number or it's a null value skip it and try to ask again and so on. In example I tried to answer truly your question. If we suppose that our age is between 1 and 150 then input value accepted, else it's a wrong value. For terminating program, the user can use 0 key and enter it as a value.
使用“while”语句直到用户输入一个真值,如果输入值不是数字或者它是一个空值,则跳过它并再次尝试询问,依此类推。例如,我试图真正回答您的问题。如果我们假设我们的年龄在 1 到 150 之间,那么输入值被接受,否则它是一个错误的值。对于终止程序,用户可以使用 0 键并将其作为值输入。
Note: Read comments top of code.
注意:阅读代码顶部的注释。
# If your input value is only a number then use "Value.isdigit() == False".
# If you need an input that is a text, you should remove "Value.isdigit() == False".
def Input(Message):
Value = None
while Value == None or Value.isdigit() == False:
try:
Value = str(input(Message)).strip()
except InputError:
Value = None
return Value
# Example:
age = 0
# If we suppose that our age is between 1 and 150 then input value accepted,
# else it's a wrong value.
while age <=0 or age >150:
age = int(Input("Please enter your age: "))
# For terminating program, the user can use 0 key and enter it as an a value.
if age == 0:
print("Terminating ...")
exit(0)
if age >= 18 and age <=150:
print("You are able to vote in the United States!")
else:
print("You are not able to vote in the United States.")
回答by Saeed Zahedian Abroodi
You can make the input statement a while True loop so it repeatedly asks for the users input and then break that loop if the user enters the response you would like. And you can use try and except blocks to handle invalid responses.
您可以使 input 语句成为 while True 循环,以便它反复询问用户输入,然后在用户输入您想要的响应时中断该循环。您可以使用 try 和 except 块来处理无效响应。
while True:
var = True
try:
age = int(input("Please enter your age: "))
except ValueError:
print("Invalid input.")
var = False
if var == True:
if age >= 18:
print("You are able to vote in the United States.")
break
else:
print("You are not able to vote in the United States.")
The var variable is just so that if the user enters a string instead of a integer the program wont return "You are not able to vote in the United States."
var 变量只是这样,如果用户输入字符串而不是整数,程序将不会返回“您无法在美国投票”。