Попросити користувача ввести, поки він не дасть дійсну відповідь


562

Я пишу програму, яка приймає вхід від користувача.

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

Програма працює так, як очікувалося, поки користувач вводить змістовні дані.

C:\Python\Projects> canyouvote.py
Please enter your age: 23
You are able to vote in the United States!

Але це не вдається, якщо користувач вводить недійсні дані:

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'

Замість того, щоб вийти з ладу, я хотів би, щоб програма знову запитала інформацію. Подобається це:

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!

Як я можу змусити програму запитувати дійсні введення замість збоїв, коли вводяться нечуттєві дані?

Як я можу відкинути такі цінності -1, що є дійсним int, але безглуздим у цьому контексті?

Відповіді:


704

Найпростіший спосіб досягти цього - це поставити inputметод в циклі часу. Використовуйте, continueколи ви отримуєте поганий вхід, і breakвиходите з циклу, коли ви задоволені.

Коли ваш вхід може підняти виняток

Використовуйте 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.")

Реалізація власних правил перевірки

Якщо ви хочете відхилити значення, які 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): ")

Спочатку це може виглядати привабливо, оскільки він коротший, ніж while Trueметод, але він порушує принцип не повторювати себе при розробці програмного забезпечення. Це збільшує ймовірність появи помилок у вашій системі. Що робити, якщо ви хочете повернути до версії 2.7, змінивши inputна raw_input, але випадково змінити лише перше inputвище? Це SyntaxErrorпросто очікування.

Рекурсія підірве ваш стек

Якщо ви щойно дізналися про рекурсію, ви можете спокусити її використовувати, 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

Здається, це спрацьовує нормально більшу частину часу, але якщо користувач вводить недопустимі дані достатньо разів, сценарій припиняється символом a RuntimeError: maximum recursion depth exceeded. Ви можете подумати, "жоден дурень не зробив би 1000 помилок поспіль", але ви недооцінюєте винахідливість дурнів!


53
Весело читати його з багатьма прикладами, кудо. Занижений урок: "Не варто недооцінювати винахідливість дурнів!"
vpibano

3
Мало того, що я б відмовився від обох питань і запитань, оскільки вони чудові, але ви уклали угоду з "дивовижною шістькою". Молодці, @Kevin.
erekalper

1
Не оцінюйте винахідливості дурнів і розумних нападників. Напад DOS був би найпростішим для подібних речей, але можливі й інші.
Соломон Учко

Чи можемо ми використовувати новий оператор «морж» замість надмірних входів? Це також поганий стиль?
Дж. Арун Мані

1
@JArunMani Я не думаю, що це буде поганий стиль, але може бути трохи менш читабельним. У вас дійсно буде лише один inputна цикл, і цикл стане дуже коротким, але стан може стати досить довгим ...
Tomerikoo

39

Чому б ви зробили, 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 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.

це буде працювати, оскільки вік ніколи не матиме значення, яке не матиме сенсу, і код слід логіці вашого "бізнес-процесу"


22

Хоча прийнята відповідь дивовижна. Я також хотів би поділитися швидким злом для цієї проблеми. (Це також стосується негативної вікової проблеми.)

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 Цей код призначений для python 3.x.


1
Зауважте, що цей код є рекурсивним, але рекурсія тут не потрібна, і, як сказав Кевін, він може підірвати ваш стек.
PM 2Рінь

2
@ PM2Ring - ви праві. Але моя мета тут була лише показати, як "коротке замикання" може мінімізувати (прикрасити) довгі шматочки коду.
авег

11
Чому б ви присвоїли лямбда змінній, просто використовуйте defзамість цього. def f(age):набагато зрозуміліше, ніжf = lambda age:
GP89

3
У деяких випадках вам може знадобитися вік лише один раз, і тоді ця функція не використовується. Можна скористатися функцією та викинути її після виконання завдання. Також це може бути не найкращим способом, але, безумовно, це інший спосіб зробити це (що було метою мого рішення).
aaveg

@aaveg, як би ти перетворив цей код, щоб насправді зберегти вік, передбачений користувачем?
Tytire Recubans

12

Отже, я нещодавно поплутався з чимось подібним до цього, і придумав таке рішення, яке використовує спосіб отримання входу, який відкидає мотлох, перш ніж це навіть перевірити будь-яким логічним способом.

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()функцією в одному модулі, щоб дозволити, скажімо, три цифри.


12

Функціональний підхід або " дивись мама без петель! ":

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.chainі itertools.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рядки з попереднього кроку до inputфункції. Наприклад:
    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. Ми використовуємо filterта str.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...
    І щоб отримати лише перші рядки, які використовуються лише для цифр, ми використовуємо next.

Інші правила перевірки:

  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

    Або, якщо вам не подобається використання методів dnder (dunder = подвійне підкреслення), ви завжди можете визначити власну функцію або використовувати ті з operatorмодуля.

  4. Існування шляху:
    тут можна використовувати pathlibбібліотеку та її Path.existsметод:

    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. Це може поєднуватися із наданням функції за замовчуванням для nextфункції:

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!

Попередня обробка вхідних даних:

Іноді ми не хочемо відхиляти введення даних, якщо користувач випадково надав його IN CAPS або з пробілом на початку або в кінці рядка. Для врахування цих простих помилок ми можемо попередньо обробити вхідні дані за допомогою застосувань str.lowerта str.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

На жаль, якщо комусь потрібне спеціальне повідомлення для кожного невдалого випадку, то, боюся, немає досить функціонального способу. Або, принаймні, я не міг її знайти.


Яка ґрунтовна та чудова відповідь, розбиття пояснень було чудовим.
Локан

Використовуючи ваш стиль, як би ви могли зайнятись тим, щоб позбавити пробілу та зменшити обробку даних для тестування членства? Я не хочу створювати набір, який повинен включати як верхні, так і малі приклади. Я також хотів би допустити помилки введення пробілів.
Остін

1
@Austin Я додав новий розділ щодо попередньої обробки. Поглянь.
Георгій

Це нагадує мені про ReactiveX. Але, можливо, на це надихнули функціональні мови в першу чергу?
Mateen Ulhaq

8

Використання клацання :

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

Ми також можемо вказати лише один із обмежень minабо max:

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

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

2

Опираючись на чудові пропозиції Даніеля К і Патріка Артнера, тут є ще більш узагальнене рішення.

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

Я вибрав явні ifта raiseтвердження замість an assert, тому що перевірка тверджень може бути вимкнена, тоді як перевірка тверджень завжди повинна бути забезпечена надійністю.

Це може бути використано для отримання різних видів введення з різними умовами перевірки. Наприклад:

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

1

Спробуйте це: -

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

0

Хоча 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.")

0

Хороше питання! Ви можете спробувати наступний код для цього. =)

Цей код використовує ast.literal_eval () для пошуку типу даних введення ( age). Потім слід наступний алгоритм:

  1. Попросіть користувача ввести її / його age.

    1.1. Якщо ageє floatабо intтип даних:

    • Перевірте, чи age>=18. Якщо age>=18, надрукуйте відповідний вихід і вихід.

    • Перевірте, чи 0<age<18. Якщо 0<age<18, надрукуйте відповідний вихід і вихід.

    • Якщо age<=0попросіть користувача знову ввести дійсне число для віку ( тобто повернутися до кроку 1)

    1.2. Якщо ageнемає floatабо intтип даних, то попросіть користувача знову ввести його / його вік ( тобто повернутися до кроку 1)

Ось код.

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

0

Ви завжди можете застосувати просту логіку if-else і додати ще одну ifлогіку до свого коду разом із forциклом.

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

Ви можете написати більш загальну логіку, щоб дозволити користувачеві вводити лише певну кількість разів, оскільки той самий випадок використання виникає у багатьох реальних програмах.

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.

1
ви забудете збільшувати значення iCount після кожного циклу
Hoai-Thu Vuong,

-1

Ви можете зробити операцію введення деякий час циклом True, щоб він неодноразово просив ввести користувачів, а потім розірвати цю петлю, якщо користувач введе відповідь, яку ви хочете. І ви можете використовувати спробувати, крім блоків, для обробки недійсних відповідей.

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

Використовуйте оператор "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 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.")

-1

Ще одне рішення для використання перевірки вхідних даних за допомогою спеціалізованої 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

-1

Ось більш чисте, узагальнене рішення, що дозволяє уникнути повторюваних блоків if / else: запишіть у словник функцію, яка займає (помилка, запит про помилку), і виконайте всі ваші перевірки значень із твердженнями.

def validate_input(prompt, error_map):
    while True:
        try:
            data = int(input(prompt))
            # Insert your non-exception-throwing conditionals here
            assert data > 0
            return data
        # Print whatever text you want the user to see
        # depending on how they messed up
        except tuple(error_map.keys()) as e:
            print(error_map[type(e)])

Використання:

d = {ValueError: 'Integers only', AssertionError: 'Positive numbers only', 
     KeyboardInterrupt: 'You can never leave'}
user_input = validate_input("Positive number: ", d)

-1

Постійний ввід користувача за допомогою рекурсивної функції :

Рядок

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)

-2

Простим рішенням було б:

while True:
    age = int(input("Please enter your age: "))

    if (age<=0) or (age>120):
        print('Sorry, I did not understand that.Please try again')
        continue
    else:

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

Пояснення вищевказаного коду: Для дійсного віку він повинен бути позитивним і не повинен перевищувати нормального фізичного віку, скажімо, наприклад, максимальний вік становить 120 років.

Тоді ми можемо запитати користувача про вік, і якщо віковий показник від'ємний або більше 120, ми вважаємо його недійсним та просимо користувача спробувати ще раз.

Після введення дійсного вводу ми проводимо перевірку (використовуючи вкладене твердження if-else), чи вік> = 18, або навпаки та друкуємо повідомлення, чи має право користувач голосувати


"Будь ласка, введіть свій вік: дикість шість" ': та сама аварія, як зазначено у питанні ...
BDL

-2

прийміть введення як рядок і використовуйте isdigit (), щоб перевірити, чи вхід має лише цифри, а не порожній, не може бути -ve

while(True):
   #take input as string
   name = input('Enter age : ')
   #check if valid age, only digits
   print( name.isdigit() ) 

run output : 
Enter age : 12
True
Enter age : 
False
Enter age : qwd
False
Enter age : dw3
False
Enter age : 21de
False
Enter age : 1
True
Enter age : -1
False


Це також не відповідає на питання.
Георгій
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.