要求用户输入,直到他们给出有效的响应

Asking the user for input until they give a valid response

提问人: 提问时间:4/25/2014 最后编辑:14 revs, 10 users 55%Kevin 更新时间:5/23/2022 访问量:1024767

问:

我正在编写一个接受用户输入的程序。

#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.")

只要用户输入有意义的数据,程序就会按预期工作。

Please enter your age: 23
You are able to vote in the United States!

但是,如果用户输入无效数据,则失败:

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'

我希望程序再次请求输入,而不是崩溃。喜欢这个:

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!

如何请求有效输入而不是崩溃或接受无效值(例如)?-1

Python 验证 输入

评论


答:

989赞 9 revs, 7 users 73%Kevin #1

实现此目的的最简单方法是将方法置于 while 循环中。当您得到错误的输入时,请使用 continue,当您满意时,请使用 outinputbreak

当您的输入可能引发异常时

使用 tryexcept 检测用户何时输入无法分析的数据。

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

实现自己的验证规则

如果要拒绝 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

结合异常处理和自定义验证

上述两种技术都可以组合成一个循环。

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

将其全部封装在一个函数中

如果需要向用户询问许多不同的值,则将此代码放在函数中可能很有用,这样您就不必每次都重新键入它。

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

把它们放在一起

你可以扩展这个想法,做一个非常通用的输入函数:

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:
                    expected = " or ".join((
                        ", ".join(str(x) for x in range_[:-1]),
                        str(range_[-1])
                    ))
                    print(template.format(expected))
        else:
            return ui

用法如下:

age = sanitised_input("Enter your age: ", int, 1, 101)
answer = sanitised_input("Enter your answer: ", str.lower, range_=('a', 'b', 'c', 'd'))

常见的陷阱,以及为什么你应该避免它们

冗余语句的冗余使用input

这种方法有效,但通常被认为是糟糕的风格:

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

它最初可能看起来很有吸引力,因为它比方法短,但它违反了软件开发的“不要重复自己”原则。这增加了系统中出现错误的可能性。如果您想通过更改为 向后移植到 2.7,但不小心只更改了上面的第一个怎么办?这只是一个等待发生的过程。while Trueinputraw_inputinputSyntaxError

递归会破坏你的堆栈

如果你刚刚学习了递归,你可能会想使用它,这样你就可以处理 while 循环。get_non_negative_int

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

这似乎在大多数情况下工作正常,但如果用户输入无效数据的次数足够多,则脚本将终止并显示 .你可能会认为“没有一个傻瓜会连续犯1000个错误”,但你低估了傻瓜的聪明才智!RuntimeError: maximum recursion depth exceeded

评论

2赞 Solomon Ucko 4/28/2019
不要估计傻瓜的聪明才智......和聪明的攻击者。对于这种事情,DOS攻击是最容易的,但其他攻击可能是可能的。
0赞 J Arun Mani 4/16/2020
我们可以使用新的“海象”运算符代替冗余输入吗?这也是一种糟糕的风格吗?
1赞 Tomerikoo 5/9/2020
@JArunMani我不认为它的风格会很差,但可读性可能会降低一些。你确实每个循环只有一个,循环会变得非常短,但条件可能会变得很长......input
0赞 laundmo 7/24/2020
您能否将此许可证指定为公共领域或 MIT(甚至在您的个人资料中指定),这样就没有人担心不允许在非 CC 作品中使用的 CC 许可证?
6赞 Kevin 7/24/2020
@laundmo,当然,我发布了我写到公共领域的代码块。在任何情况下都可以随意使用它们,而无需我的明确许可或知情。关于非代码块部分,如果你想把我的整个答案粘贴到你正在写的一本“学习 Python”的书中,让我们谈谈版税;-)
30赞 2 revs, 2 users 78%aaveg #2

虽然公认的答案是惊人的。我还想分享一个针对这个问题的快速技巧。(这也解决了负年龄问题。

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. 此代码适用于 python 3.x。

评论

1赞 PM 2Ring 1/31/2016
请注意,这段代码是递归的,但这里不需要递归,正如 Kevin 所说,它可能会破坏你的堆栈。
3赞 aaveg 2/3/2016
@PM2Ring - 你是对的。但我在这里的目的只是展示“短路”如何最小化(美化)长代码片段。
15赞 GP89 5/17/2017
为什么要将 lambda 分配给变量,只需改用即可。 远比defdef f(age):f = lambda age:
4赞 aaveg 5/17/2017
在某些情况下,您可能只需要一次年龄,然后就无法使用该功能。人们可能想使用一个函数,并在工作完成后将其扔掉。此外,这可能不是最好的方法,但它绝对是一种不同的方法(这是我的解决方案的目的)。
0赞 Tytire Recubans 7/5/2019
@aaveg,您将如何转换此代码以实际保存用户提供的年龄?
63赞 2 revs, 2 users 96%Steven Stip #3

你为什么要做一个然后打破这个循环,同时你也可以把你的要求放在while语句中,因为你只想在你有年龄后停止?while True

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

这将导致以下结果:

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.

这将起作用,因为 age 永远不会有一个没有意义的值,并且代码遵循您的“业务流程”的逻辑

评论

0赞 hc_dev 6/18/2022
这里建议的精心设计的退出条件🤩️避免了由于没有安全到达中断或返回而导致的无限循环的陷阱。while True
16赞 3 revscat #4

所以,我最近搞砸了类似的东西,我想出了以下解决方案,它使用一种拒绝垃圾的输入方式,甚至在以任何合乎逻辑的方式检查它之前。

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()

您可以在此处找到完整的模块。

例:

$ ./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!
$ _

请注意,此实现的本质是,一旦读取到不是数字的内容,它就会关闭 stdin。我没有在之后按回车键,但我需要在数字之后按回车键。a

您可以将其与同一模块中的函数合并,以仅允许三位数字。thismany()

评论

0赞 Kebman 10/31/2020
如果您已经检测到击键,为什么完全允许字符并抛出错误,而您可以默默地忽略它们,直到获得所需的数字?
1赞 cat 11/1/2020
@Kebman您可以这样做,但对用户来说,他们可以键入的内容可能不太明显
-1赞 2 revs2Cubed #5

虽然 / 块可以工作,但完成此任务的更快、更干净的方法是使用 .tryexceptstr.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.")
5赞 ojas mohril #6
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."
-1赞 Mangu Singh Rajpurohit #7

您可以编写更通用的逻辑,以允许用户仅输入特定次数,因为在许多实际应用程序中会出现相同的用例。

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.

评论

2赞 Hoai-Thu Vuong 3/1/2017
您忘记在每次循环后增加 iCount 值
3赞 Pratik Anand #8

试试这个:-

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')
3赞 6 revs, 2 users 99%Saeed Zahedian Abroodi #9

使用“while”语句,直到用户输入一个真正的值,如果输入值不是数字或它是空值,请跳过它并尝试再次询问,依此类推。 例如,我试图真正回答你的问题。如果我们假设我们的年龄在 1 到 150 岁之间,那么输入值就被接受,否则它就是一个错误的值。 对于终止程序,用户可以使用 0 键并将其输入为值。

注意:阅读代码顶部的注释。

# 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 Exception:
            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.")
-1赞 user9142415 #10

您可以将输入语句设置为 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.")

var 变量只是为了让用户输入字符串而不是整数,程序不会返回“您无法在美国投票”。

-1赞 3 revsPatrick Artner #11

另一种解决方案是使用整数输入的自定义和(可选)范围验证来使用输入验证:ValidationError

class ValidationError(ValueError): 
    """Special validation error - its message is supposed to be printed"""
    pass

def RangeValidator(text,num,r):
    """Generic validator - raises 'text' as ValidationError if 'num' not in range 'r'."""
    if num in r:
        return num
    raise ValidationError(text)

def ValidCol(c): 
    """Specialized column validator providing text and range."""
    return RangeValidator("Columns must be in the range of 0 to 3 (inclusive)", 
                          c, range(4))

def ValidRow(r): 
    """Specialized row validator providing text and range."""
    return RangeValidator("Rows must be in the range of 5 to 15(exclusive)",
                          r, range(5,15))

用法:

def GetInt(text, validator=None):
    """Aks user for integer input until a valid integer is given. If provided, 
    a 'validator' function takes the integer and either raises a 
    ValidationError to be printed or returns the valid number. 
    Non integers display a simple error message."""
    print()
    while True:
        n = input(text)
        try:
            n = int(n)

            return n if validator is None else validator(n)

        except ValueError as ve:
            # prints ValidationErrors directly - else generic message:
            if isinstance(ve, ValidationError):
                print(ve)
            else:
                print("Invalid input: ", n)


column = GetInt("Pleased enter column: ", ValidCol)
row = GetInt("Pleased enter row: ", ValidRow)
print( row, column)

输出:

Pleased enter column: 22
Columns must be in the range of 0 to 3 (inclusive)
Pleased enter column: -2
Columns must be in the range of 0 to 3 (inclusive)
Pleased enter column: 2
Pleased enter row: a
Invalid input:  a
Pleased enter row: 72
Rows must be in the range of 5 to 15(exclusive)
Pleased enter row: 9  

9, 2
6赞 3 revsJoão Manuel Rodrigues #12

在 Daniel Q 和 Patrick Artner 的出色建议的基础上, 这是一个更通用的解决方案。

# Assuming Python3
import sys

class ValidationError(ValueError):  # thanks Patrick Artner
    pass

def validate_input(prompt, cast=str, cond=(lambda x: True), onerror=None):
    if onerror==None: onerror = {}
    while True:
        try:
            data = cast(input(prompt))
            if not cond(data): raise ValidationError
            return data
        except tuple(onerror.keys()) as e:  # thanks Daniel Q
            print(onerror[type(e)], file=sys.stderr)

我选择了显式和语句,而不是 , 因为断言检查可能被关闭, 而验证应始终处于开启状态以提供稳健性。ifraiseassert

这可用于获取不同类型的输入, 具有不同的验证条件。 例如:

# No validation, equivalent to simple input:
anystr = validate_input("Enter any string: ")

# Get a string containing only letters:
letters = validate_input("Enter letters: ",
    cond=str.isalpha,
    onerror={ValidationError: "Only letters, please!"})

# Get a float in [0, 100]:
percentage = validate_input("Percentage? ",
    cast=float, cond=lambda x: 0.0<=x<=100.0,
    onerror={ValidationError: "Must be between 0 and 100!",
             ValueError: "Not a number!"})

或者,回答原始问题:

age = validate_input("Please enter your age: ",
        cast=int, cond=lambda a:0<=a<150,
        onerror={ValidationError: "Enter a plausible age, please!",
                 ValueError: "Enter an integer, please!"})
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.")
4赞 4 revsSiddharth Satpathy #13

问得好!为此,您可以尝试以下代码。=)

此代码使用 ast.literal_eval() 查找输入 () 的数据类型。然后它遵循以下算法:age

  1. 要求用户输入她/他的 .age

    1.1. 如果是或数据类型:agefloatint

    • 检查是否 .如果 ,打印相应的输出并退出。age>=18age>=18

    • 检查是否 .如果 ,打印相应的输出并退出。0<age<180<age<18

    • 如果 ,请要求用户再次输入一个有效的年龄数字,(返回步骤 1。age<=0

    1.2. 如果不是或数据类型,则要求用户再次输入她/他的年龄(返回步骤 1。agefloatint

这是代码。

from ast import literal_eval

''' This function is used to identify the data type of input data.'''
def input_type(input_data):
    try:
        return type(literal_eval(input_data))
    except (ValueError, SyntaxError):
        return str

flag = True

while(flag):
    age = raw_input("Please enter your age: ")

    if input_type(age)==float or input_type(age)==int:
        if eval(age)>=18: 
            print("You are able to vote in the United States!") 
            flag = False 
        elif eval(age)>0 and eval(age)<18: 
            print("You are not able to vote in the United States.") 
            flag = False
        else: print("Please enter a valid number as your age.")

    else: print("Sorry, I didn't understand that.") 
-1赞 4 revsRoko C. Buljan #14

使用递归函数的持久用户输入:

字符串

def askName():
    return input("Write your name: ").strip() or askName()

name = askName()

整数

def askAge():
    try: return int(input("Enter your age: "))
    except ValueError: return askAge()

age = askAge()

最后,问题要求:

def askAge():
    try: return int(input("Enter your age: "))
    except ValueError: return askAge()

age = askAge()

responseAge = [
    "You are able to vote in the United States!",
    "You are not able to vote in the United States.",
][int(age < 18)]

print(responseAge)
42赞 3 revsGeorgy #15

功能方法或“看,妈妈没有循环!

from itertools import chain, repeat

prompts = chain(["Enter a number: "], repeat("Not a number! Try again: "))
replies = map(input, prompts)
valid_response = next(filter(str.isdigit, replies))
print(valid_response)
Enter a number:  a
Not a number! Try again:  b
Not a number! Try again:  1
1

或者,如果您希望将“错误输入”消息与输入提示分开,就像在其他答案中一样:

prompt_msg = "Enter a number: "
bad_input_msg = "Sorry, I didn't understand that."
prompts = chain([prompt_msg], repeat('\n'.join([bad_input_msg, prompt_msg])))
replies = map(input, prompts)
valid_response = next(filter(str.isdigit, replies))
print(valid_response)
Enter a number:  a
Sorry, I didn't understand that.
Enter a number:  b
Sorry, I didn't understand that.
Enter a number:  1
1

它是如何工作的?

  1. prompts = chain(["Enter a number: "], repeat("Not a number! Try again: "))
    
    itertools.chainitertools.repeat 的这种组合将创建一个迭代器 这将产生一次字符串,以及无限次:"Enter a number: ""Not a number! Try again: "
    for prompt in prompts:
        print(prompt)
    
    Enter a number: 
    Not a number! Try again: 
    Not a number! Try again: 
    Not a number! Try again: 
    # ... and so on
    
  2. replies = map(input, prompts)- 这里 Map 会将上一步中的所有字符串应用到输入函数中。例如:prompts
    for reply in replies:
        print(reply)
    
    Enter a number:  a
    a
    Not a number! Try again:  1
    1
    Not a number! Try again:  it doesn't care now
    it doesn't care now
    # and so on...
    
  3. 我们使用 filterstr.isdigit 来过滤掉那些只包含数字的字符串: 为了只获取第一个数字的字符串,我们接下来使用。
    only_digits = filter(str.isdigit, replies)
    for reply in only_digits:
        print(reply)
    
    Enter a number:  a
    Not a number! Try again:  1
    1
    Not a number! Try again:  2
    2
    Not a number! Try again:  b
    Not a number! Try again: # and so on...
    

其他验证规则:

  1. 字符串方法:当然,您可以使用其他字符串方法,例如 str.isalpha 仅获取字母字符串,或者 str.isupper获取大写字母。有关完整列表,请参阅文档

  2. 成员资格测试:
    有几种不同的方法可以执行它。其中之一是使用__contains__方法:

    from itertools import chain, repeat
    
    fruits = {'apple', 'orange', 'peach'}
    prompts = chain(["Enter a fruit: "], repeat("I don't know this one! Try again: "))
    replies = map(input, prompts)
    valid_response = next(filter(fruits.__contains__, replies))
    print(valid_response)
    
    Enter a fruit:  1
    I don't know this one! Try again:  foo
    I don't know this one! Try again:  apple
    apple
    
  3. 数字比较:
    我们可以在这里使用一些有用的比较方法。例如,对于__lt__ ():
    <

    from itertools import chain, repeat
    
    prompts = chain(["Enter a positive number:"], repeat("I need a positive number! Try again:"))
    replies = map(input, prompts)
    numeric_strings = filter(str.isnumeric, replies)
    numbers = map(float, numeric_strings)
    is_positive = (0.).__lt__
    valid_response = next(filter(is_positive, numbers))
    print(valid_response)
    
    Enter a positive number: a
    I need a positive number! Try again: -5
    I need a positive number! Try again: 0
    I need a positive number! Try again: 5
    5.0
    

    或者,如果您不喜欢使用 dunder 方法(dunder = 双下划线),您可以随时定义自己的函数,或者使用运算符模块中的函数。

  4. 路径存在:
    这里可以使用库及其 Path.exists 方法:
    pathlib

    from itertools import chain, repeat
    from pathlib import Path
    
    prompts = chain(["Enter a path: "], repeat("This path doesn't exist! Try again: "))
    replies = map(input, prompts)
    paths = map(Path, replies)
    valid_response = next(filter(Path.exists, paths))
    print(valid_response)
    
    Enter a path:  a b c
    This path doesn't exist! Try again:  1
    This path doesn't exist! Try again:  existing_file.txt
    existing_file.txt
    

限制尝试次数:

如果你不想通过无限次地问用户某件事来折磨他,你可以在 itertools.repeat 的调用中指定一个限制。这可以与为下一个函数提供默认值结合使用:

from itertools import chain, repeat

prompts = chain(["Enter a number:"], repeat("Not a number! Try again:", 2))
replies = map(input, prompts)
valid_response = next(filter(str.isdigit, replies), None)
print("You've failed miserably!" if valid_response is None else 'Well done!')
Enter a number: a
Not a number! Try again: b
Not a number! Try again: c
You've failed miserably!

预处理输入数据:

有时,如果用户不小心以大写字母或字符串开头或结尾带有空格,我们不想拒绝输入。考虑到这些简单的错误,我们可以通过应用 str.lowerstr.strip 方法对输入数据进行预处理。例如,对于成员资格测试的情况,代码将如下所示:

from itertools import chain, repeat

fruits = {'apple', 'orange', 'peach'}
prompts = chain(["Enter a fruit: "], repeat("I don't know this one! Try again: "))
replies = map(input, prompts)
lowercased_replies = map(str.lower, replies)
stripped_replies = map(str.strip, lowercased_replies)
valid_response = next(filter(fruits.__contains__, stripped_replies))
print(valid_response)
Enter a fruit:  duck
I don't know this one! Try again:     Orange
orange

如果有许多函数要用于预处理,则使用执行函数组合的函数可能更容易。例如,使用此处的那个:

from itertools import chain, repeat

from lz.functional import compose

fruits = {'apple', 'orange', 'peach'}
prompts = chain(["Enter a fruit: "], repeat("I don't know this one! Try again: "))
replies = map(input, prompts)
process = compose(str.strip, str.lower)  # you can add more functions here
processed_replies = map(process, replies)
valid_response = next(filter(fruits.__contains__, processed_replies))
print(valid_response)
Enter a fruit:  potato
I don't know this one! Try again:   PEACH
peach

组合验证规则:

对于一个简单的情况,例如,当程序要求年龄在 1 到 120 之间时,可以添加另一个:filter

from itertools import chain, repeat

prompt_msg = "Enter your age (1-120): "
bad_input_msg = "Wrong input."
prompts = chain([prompt_msg], repeat('\n'.join([bad_input_msg, prompt_msg])))
replies = map(input, prompts)
numeric_replies = filter(str.isdigit, replies)
ages = map(int, numeric_replies)
positive_ages = filter((0).__lt__, ages)
not_too_big_ages = filter((120).__ge__, positive_ages)
valid_response = next(not_too_big_ages)
print(valid_response)

但是在规则很多的情况下,最好实现一个执行逻辑连词的函数。在下面的示例中,我将从这里使用一个现成的:

from functools import partial
from itertools import chain, repeat

from lz.logical import conjoin


def is_one_letter(string: str) -> bool:
    return len(string) == 1


rules = [str.isalpha, str.isupper, is_one_letter, 'C'.__le__, 'P'.__ge__]

prompt_msg = "Enter a letter (C-P): "
bad_input_msg = "Wrong input."
prompts = chain([prompt_msg], repeat('\n'.join([bad_input_msg, prompt_msg])))
replies = map(input, prompts)
valid_response = next(filter(conjoin(*rules), replies))
print(valid_response)
Enter a letter (C-P):  5
Wrong input.
Enter a letter (C-P):  f
Wrong input.
Enter a letter (C-P):  CDE
Wrong input.
Enter a letter (C-P):  Q
Wrong input.
Enter a letter (C-P):  N
N

不幸的是,如果有人需要为每个失败的案例提供自定义消息,那么,恐怕没有非常实用的方法。或者,至少,我找不到。

23赞 2 revsGeorgy #16

使用 Click

Click 是一个用于命令行界面的库,它提供了向用户请求有效响应的功能。

简单示例:

import click

number = click.prompt('Please enter a number', type=float)
print(number)
Please enter a number: 
 a
Error: a is not a valid floating point value
Please enter a number: 
 10
10.0

请注意它如何自动将字符串值转换为浮点数。

检查值是否在范围内:

提供了不同的自定义类型。要获得特定范围内的数字,我们可以使用:IntRange

age = click.prompt("What's your age?", type=click.IntRange(1, 120))
print(age)
What's your age?: 
 a
Error: a is not a valid integer
What's your age?: 
 0
Error: 0 is not in the valid range of 1 to 120.
What's your age?: 
 5
5

我们也可以只指定其中一个限制,或者:minmax

age = click.prompt("What's your age?", type=click.IntRange(min=14))
print(age)
What's your age?: 
 0
Error: 0 is smaller than the minimum valid value 14.
What's your age?: 
 18
18

会员测试:

使用类型。默认情况下,此检查区分大小写。click.Choice

choices = {'apple', 'orange', 'peach'}
choice = click.prompt('Provide a fruit', type=click.Choice(choices, case_sensitive=False))
print(choice)
Provide a fruit (apple, peach, orange): 
 banana
Error: invalid choice: banana. (choose from apple, peach, orange)
Provide a fruit (apple, peach, orange): 
 OrAnGe
orange

使用路径和文件:

使用类型,我们可以检查现有路径并解析它们:click.Path

path = click.prompt('Provide path', type=click.Path(exists=True, resolve_path=True))
print(path)
Provide path: 
 nonexistent
Error: Path "nonexistent" does not exist.
Provide path: 
 existing_folder
'/path/to/existing_folder

读取和写入文件可以通过以下方式完成:click.File

file = click.prompt('In which file to write data?', type=click.File('w'))
with file.open():
    file.write('Hello!')
# More info about `lazy=True` at:
# https://click.palletsprojects.com/en/7.x/arguments/#file-opening-safety
file = click.prompt('Which file you wanna read?', type=click.File(lazy=True))
with file.open():
    print(file.read())
In which file to write data?: 
         # <-- provided an empty string, which is an illegal name for a file
In which file to write data?: 
 some_file.txt
Which file you wanna read?: 
 nonexistent.txt
Error: Could not open file: nonexistent.txt: No such file or directory
Which file you wanna read?: 
 some_file.txt
Hello!

其他示例:

密码确认:

password = click.prompt('Enter password', hide_input=True, confirmation_prompt=True)
print(password)
Enter password: 
 ······
Repeat for confirmation: 
 ·
Error: the two entered values do not match
Enter password: 
 ······
Repeat for confirmation: 
 ······
qwerty

默认值:

在这种情况下,只需按下(或您使用的任何键)而不输入值,就会为您提供一个默认值:Enter

number = click.prompt('Please enter a number', type=int, default=42)
print(number)
Please enter a number [42]: 
 a
Error: a is not a valid integer
Please enter a number [42]: 
 
42
0赞 2 revsEp1c1aN #17

您始终可以应用简单的 if-else 逻辑,并在代码中添加一个逻辑以及循环。iffor

while True:
     age = int(input("Please enter your age: "))
     if (age >= 18)  : 
         print("You are able to vote in the United States!")
     if (age < 18) & (age > 0):
         print("You are not able to vote in the United States.")
     else:
         print("Wrong characters, the input must be numeric")
         continue

这将是一个无限的厕所,你会被要求无限期地进入这个时代。

评论

1赞 Georgy 7/1/2019
这并不能真正回答这个问题。问题在于获取用户输入,直到他们给出有效的响应,而不是无限期。
-7赞 3 revsLiju #18

以下代码可能会有所帮助。

age=(lambda i,f: f(i,f))(input("Please enter your age: "),lambda i,f: i if i.isdigit() else f(input("Please enter your age: "),f))
print("You are able to vote in the united states" if int(age)>=18 else "You are not able to vote in the united states",end='')

如果你想获得最大的尝试次数,比如 3 次,请使用下面的代码

age=(lambda i,n,f: f(i,n,f))(input("Please enter your age: "),1,lambda i,n,f: i if i.isdigit() else (None if n==3 else f(input("Please enter your age: "),n+1,f)))
print("You are able to vote in the united states" if age and int(age)>=18 else "You are not able to vote in the united states",end='')

注意:这使用递归。

评论

1赞 ggorlen 1/28/2022
不要使用递归来收集用户输入。如果重试次数足够多,应用程序就会崩溃。我不明白高尔夫代码。为什么不让它变得通俗易懂呢?
1赞 smci 2/11/2022
教给新用户而不是简单的 while 循环是令人困惑和混乱的。
7赞 2 revs, 2 users 62%behnaz.sheikhi #19

使用 try-except 处理错误并再次重复:

while True:
    try:
        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.")
    except Exception as e:
        print("please enter number")

评论

4赞 Georgy 9/11/2020
您缺少语句,并且没有必要。breakprint("please enter number")
21赞 3 revs, 2 users 73%np8 #20

我是Unix哲学“做一件事,就做好它”的忠实拥护者。捕获用户输入并对其进行验证是两个独立的步骤:

  • 提示用户输入,直到输入正常get_input
  • 使用可传递给validatorget_input

它可以保持简单(Python 3.8+,带有 walrus 运算符):

def get_input(
    prompt="Enter a value: ",
    validator=lambda x: True,
    error_message="Invalid input. Please try again.",
):
    while not validator(value := input(prompt)):
        print(error_message)
    return value

def is_positive_int(value):
    try:
        return int(value) >= 0
    except ValueError:
        return False

if __name__ == "__main__":
    val = get_input("Give a positive number: ", is_positive_int)
    print(f"OK, thanks for {val}")

示例运行:

Give a positive number: -5
Invalid input. Please try again.
Give a positive number: asdf
Invalid input. Please try again.
Give a positive number:
Invalid input. Please try again.
Give a positive number: 42
OK, thanks for 42

在 Python < 3.8 中,您可以使用如下:get_input

def get_input(
    prompt="Enter a value: ",
    validator=lambda x: True,
    error_message="Invalid input. Please try again.",
):
    while True:
        value = input(prompt)
        if validator(value):
            return value
        print(error_message)

您还可以在终止应用程序之前处理并打印友好的退出消息。如果需要,可以使用计数器来限制允许的重试次数。KeyboardInterrupt

-2赞 3 revs, 2 users 96%Rein F #21

用于检查字符串是否表示有效的整数。isdigit()

您可以使用递归函数。

def ask():
    answer = input("Please enter amount to convert: ")
    if not answer.isdigit():
        print("Invalid")
        return ask()

    return int(answer)

Gdp = ask()

或者 while 循环

while True:
    answer = input("Please enter amount to convert: ")
    if not answer.isdigit():
        print("Invalid")
        continue

    Gbp = int(answer)

评论

1赞 Tomerikoo 5/5/2021
函数中缺少 a。你是递归调用,但那个调用又回来了......而你循环是无限的......returnreturnNonewhile
0赞 LevitatingBusinessMan 5/6/2021
@Tomerikoo 它递归地询问,直到答案有效,我认为这就是所问的。我的意思是以一种可以将任何代码放入递归函数或 while 循环的方式编写它。这实际上是为另一个问题而写的,它被标记为与这个问题重复,所以我把它贴在这里。
1赞 Tomerikoo 5/6/2021
我的意思是,你应该在某些场景中测试你的代码。在第一种情况下,可能是,在第二种情况下,可能应该有一个Gbp = int(answer)return int(answer)break
-1赞 superN0va #22

您可以尝试将其转换为整数,但如果它不起作用,请要求用户重复。

while True:
    age = input('Please enter your age: ')
    try:
        age_int = int(age)
        if age_int >= 18:
            print('You can vote in the United States!')
        else:
            print('You cannot vote in the United States.')
        break
    except:
        print('Please enter a meaningful answer.')
        

只要用户没有输入有意义的答案,while 循环就会运行,但如果有意义,则会中断。