本文翻译自:Asking the user for input until they give a valid response

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 ,但在这种情况下毫无意义?


#1楼

参考:https://stackoom.com/question/1Zk0I/要求用户提供输入-直到他们给出有效的答复


#2楼

The simplest way to accomplish this would be to put the input method in a while loop. 完成此操作的最简单方法是将input方法放入while循环中。 Use continue when you get bad input, and break out of the loop when you're satisfied. 使用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. 使用tryexcept来检测用户何时输入了无法解析的数据。

while True:try:# Note: Python 2.x users should use raw_input, the equivalent of 3.x's inputage = int(input("Please enter your age: "))except ValueError:print("Sorry, I didn't understand that.")#better try again... Return to the start of the loopcontinueelse:#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.")continueelse:#we're happy with the value given.#we're ready to exit the loop.breakwhile 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.")continueif age < 0:print("Sorry, your response must not be negative.")continueelse:#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.")continueif value < 0:print("Sorry, your response must not be negative.")continueelse:breakreturn valueage = 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__))continueif 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 Yourself principle of software development. 最初它看起来很有吸引力,因为它比while True方法短,但它违反了软件开发的“ 不要重复自己”的原理。 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? 如果你想向移植到2.7通过改变inputraw_input ,却意外地只改变第一input上面? It's a SyntaxError just waiting to happen. 这是一个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 . 这似乎在大多数情况下都可以正常工作,但是如果用户输入无效数据的次数足够多,该脚本将以RuntimeError: maximum recursion depth exceeded终止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! 您可能会认为“没有傻瓜会连续犯1000个错误”,但是您却低估了傻瓜的创造力!


#3楼

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: ")))

PS This code is for python 3.x. PS此代码适用于python3.x。


#4楼

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? 您为什么要做while True ,然后中断这个循环,而您也可以只将需求放在while语句中,因为您想要的只是一旦年龄就停止了?

age = None
while age is None:input_value = input("Please enter your age: ")try:# try and convert the string input to a numberage = int(input_value)except ValueError:# tell the user offprint("{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" 这是可行的,因为年龄永远不会有没有意义的值,并且代码遵循“业务流程”的逻辑


#5楼

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, osfd = sys.stdin.fileno()# save old stateflags_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# iflagattrs[0] &= ~(termios.IGNBRK | termios.BRKINT | termios.PARMRK| termios.ISTRIP | termios.INLCR | termios. IGNCR| termios.ICRNL | termios.IXON )# oflagattrs[1] &= ~termios.OPOST# cflagattrs[2] &= ~(termios.CSIZE | termios. PARENB)attrs[2] |= termios.CS8# lflagattrs[3] &= ~(termios.ECHONL | termios.ECHO | termios.ICANON| termios.ISIG | termios.IEXTEN)termios.tcsetattr(fd, termios.TCSANOW, attrs)# turn off non-blockingfcntl.fcntl(fd, fcntl.F_SETFL, flags_save & ~os.O_NONBLOCK)# read a single keystroketry:ret = sys.stdin.read(1) # returns a single characterexcept KeyboardInterrupt:ret = 0finally:# restore old statetermios.tcsetattr(fd, termios.TCSAFLUSH, attrs_save)fcntl.fcntl(fd, fcntl.F_SETFL, flags_save)return retdef until_not_multi(chars) -> str:"""read stdin until !(chars)"""import syschars = list(chars)y = ""sys.stdout.flush()while True:i = read_single_keypress()_ = sys.stdout.write(i)sys.stdout.flush()if i not in chars:breaky += ireturn ydef _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.")returnprint("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. 请注意,此实现的性质是,一旦读取到的不是数字,它就会关闭stdin。 I didn't hit enter after a , but I needed to after the numbers. 我没有在后面a回车,但我需要在数字后面。

You could merge this with the thismany() function in the same module to only allow, say, three digits. 您可以将其与thismany()函数合并到同一模块中,以仅允许输入三位数。


#6楼

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)breakelse: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.")

要求用户提供输入,直到他们给出有效的答复相关推荐

  1. 练习题:让用户提供半径,然后计算出对应圆的周长和面积

    import math r = float(input("输入一个半径r=")) c = math.pi*r*2 s = math.pi*r**2 print("圆的周长 ...

  2. 用户输入一个整数,求出它的各个位数,并求各位数之和

    import java.util.Scanner; //用户输入一个整数,求出它的各个位数,并求各位数之和 public class splitInteger { public static void ...

  3. 运输公司对用户计算运费.路程越远每公里运费越低.每公里每吨货物的基本运费p = 3;用户需要输入货物重量w和距离s;根据距离的不同折扣d不同(具体见)下面的表格,要求根据用户输入的w和s,计算出总运费

    /* 1.运输公司对用户计算运费.路程越远每公里运费越低. 每公里每吨货物的基本运费p = 3:用户需要输入货 物重量w和距离s:根据距离的不同折扣d不同(具体见) 下面的表格,要求根据用户输入的w和 ...

  4. 在python中输入10个整数并求出最大值_python练习题 :用户任意输入10个整数到列表中,然后由大到小排列并输出。...

    一:填空题: 1.python是一种面向_对象 __的高级语言. 2.python可以在多种平台运行,这体现了python的___可移植___特性. 3.python源代码被解释器转换后的格式为___ ...

  5. 使用DataGridView数据窗口控件,构建用户快速输入体验

    使用DataGridView数据窗口控件,构建用户快速输入体验 在"随风飘散" 博客里面,介绍了一个不错的DataGridView数据窗口控件<DataGridView数据窗 ...

  6. 为用户提供确定性——互联网平台建设

    本文包括人与人沟通.机器与机器沟通都要实现确定性:人机交互要实现确定性:通过系统能力为平台人机交互提供确定性:三个应用场景加深理解:保险产品的确定性比任何产品都重要五部分. 以下是数字化转型的分享线路 ...

  7. c语言处理用户错误输入,C语言实现用户输入

    用户输入一个字符串然后回车表示结束.因为用户在输入的过程中长度是不确定的,所以要求自己使用的循环写的更好.在这里自己写了一个代码,效率不高,相对来说如果能模拟出C++中的vector向量可能会好一些. ...

  8. python输入10个整数_python练习:编写一个程序,要求用户输入10个整数,然后输出其中最大的奇数,如果用户没有输入奇数,则输出一个消息进行说明。...

    python练习:编写一个程序,要求用户输入10个整数,然后输出其中最大的奇数,如果用户没有输入奇数,则输出一个消息进行说明. 重难点:通过input函数输入的行消息为字符串格式,必须转换为整型,否则 ...

  9. java esapi_java – 用户提供的url属性的ESAPI XSS预防

    我的一个REST API期望一个属性"url",它希望URL作为用户的输入.我正在使用ESAPI来防止XSS攻击.问题是用户提供的URL就像 http://example.com/ ...

最新文章

  1. vue 实现动态增加输入框_vue实现一个6个输入框的验证码输入组件
  2. iPhone卖不出去 采购未达标 苹果“补偿”三星6.83亿美元
  3. 20180312顺序查找
  4. 单个正态总体均值的区间估计_数理统计第20讲(单一正态总体区间估计)
  5. WEB前端之网页设计①----最新最全详解/网页基础结构
  6. VBA的userform 相关的基本事件,方法和属性,以及 userform.name 使用规范备忘
  7. Chinese NER Using Lattice LSTM 论文解读
  8. 国科大学习资料--最优化计算方法(王晓)--第五次作业答案
  9. 【自然语言处理】gensim的word2vec
  10. android studio改字体,在Android Studio中更改字体样式的不同方法有哪些
  11. java中集合的基础知识_javaSE基础知识之集合类
  12. 转行软件测试4年,从初级入门到高级测试,听听他的经验分享
  13. Flutter 设置圆角,边框
  14. Cris 的 Spark Streaming 笔记
  15. 老男孩培训 | 0基础五个月,让我收获了满意的工作和生活!
  16. VMware虚拟机的快照了解一下
  17. 远程答题的存在对于游戏工作室的重要性
  18. 台达DVP系列PLC与台达DTA温控器modbus通讯案例功能:采用台达DVP ES型号PLC,对台达DTA温控器通过485方式,modbus协议,进行温度的设定
  19. RouterOS系统安装和简单配置
  20. 神经网络中的遗传算法

热门文章

  1. Java ExecutorService四种线程池的例子与说明
  2. Binder跨进程通信原理(二):内存映射mmap原理分析
  3. 分析Android :java.lang.UnsatisfiedLinkError: dlopen failed * is 32-bit instead of 64-bit
  4. build.gradle
  5. UITableView 调整 Header 层级关系
  6. 读书狂想之《穷爸爸,富爸爸》财富观
  7. swift_038(Swift之guard关键字(守护))
  8. rs485协议_你知道HART和RS485协议的区别吗?
  9. 同一账户同时只能在一处登陆(单点登陆)
  10. yum命令安装jdk