Pytanie użytkownika o wprowadzenie danych, dopóki nie poda prawidłowej odpowiedzi

Apr 25 2014

Piszę program, który przyjmuje dane wejściowe od użytkownika.

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

Program działa zgodnie z oczekiwaniami, o ile użytkownik wprowadzi znaczące dane.

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

Ale kończy się niepowodzeniem, jeśli użytkownik wprowadzi nieprawidłowe dane:

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'

Zamiast się zawieszać, chciałbym, aby program ponownie poprosił o wprowadzenie danych. Lubię to:

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!

Jak sprawić, by program poprosił o prawidłowe dane wejściowe zamiast zawieszać się, gdy wprowadzane są niesensowne dane?

Jak mogę odrzucić wartości takie jak -1, co jest poprawne int, ale bezsensowne w tym kontekście?

Odpowiedzi

776 Kevin Apr 25 2014 at 20:31

Najprostszym sposobem osiągnięcia tego jest umieszczenie inputmetody w pętli while. Użyj, continuegdy otrzymujesz złe dane wejściowe i breakwyjdziesz z pętli, gdy jesteś zadowolony.

Kiedy Twój wkład może wywołać wyjątek

Użyj tryi,except aby wykryć, kiedy użytkownik wprowadza dane, których nie można przeanalizować.

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

Wdrażanie własnych reguł walidacji

Jeśli chcesz odrzucić wartości, które Python może pomyślnie przeanalizować, możesz dodać własną logikę walidacji.

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

Połączenie obsługi wyjątków i walidacji niestandardowej

Obie powyższe techniki można połączyć w jedną pętlę.

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

Hermetyzowanie tego wszystkiego w funkcji

Jeśli chcesz zapytać użytkownika o wiele różnych wartości, przydatne może być umieszczenie tego kodu w funkcji, aby nie trzeba było za każdym razem go wpisywać ponownie.

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

Kładąc wszystko razem

Możesz rozszerzyć ten pomysł, aby utworzyć bardzo ogólną funkcję wejściową:

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

Przy zastosowaniu takim jak:

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

Typowe pułapki i dlaczego należy ich unikać

Nadmiarowe użycie zbędnych inputinstrukcji

Ta metoda działa, ale ogólnie jest uważana za kiepską:

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

Początkowo może wyglądać atrakcyjnie, ponieważ jest krótsza niż while Truemetoda, ale narusza zasadę „ Nie powtarzaj się ” w tworzeniu oprogramowania. Zwiększa to prawdopodobieństwo błędów w systemie. A co jeśli chcesz przenieść wstecz do wersji 2.7, zmieniając inputna raw_input, ale przypadkowo zmienić tylko pierwszą z inputpowyższych? To SyntaxErrortylko czekanie, aż się wydarzy.

Rekurencja rozwali Twój stos

Jeśli właśnie nauczyłeś się rekurencji, możesz ulec pokusie, aby użyć jej w programie, get_non_negative_intaby pozbyć się pętli 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

Wydaje się, że działa to dobrze przez większość czasu, ale jeśli użytkownik wprowadzi nieprawidłowe dane wystarczająco dużo razy, skrypt zakończy działanie z rozszerzeniem RuntimeError: maximum recursion depth exceeded. Możesz pomyśleć, że „żaden głupiec nie popełniłby 1000 błędów z rzędu”, ale nie doceniasz pomysłowości głupców!

46 StevenStip Jan 14 2016 at 19:43

Dlaczego miałbyś zrobić a, while Truea potem wyrwać się z tej pętli, podczas gdy możesz po prostu umieścić swoje wymagania w instrukcji while, skoro wszystko, czego chcesz, to przestać, gdy osiągniesz wiek?

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

Skutkowałoby to:

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.

to zadziała, ponieważ wiek nigdy nie będzie miał wartości, która nie będzie miała sensu, a kod jest zgodny z logiką Twojego „procesu biznesowego”

26 aaveg Jun 29 2015 at 06:29

Chociaż przyjęta odpowiedź jest niesamowita. Chciałbym również podzielić się krótką wskazówką dotyczącą tego problemu. (To rozwiązuje również problem negatywnego wieku).

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 Ten kod jest przeznaczony dla Pythona 3.x.

17 Georgy May 10 2019 at 23:47

Podejście funkcjonalne lub „ patrz mamo bez pętli! ”:

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

lub jeśli chcesz, aby komunikat „złe dane wejściowe” był oddzielony od monitu o wprowadzenie danych, jak w innych odpowiedziach:

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

Jak to działa?

  1. prompts = chain(["Enter a number: "], repeat("Not a number! Try again: "))
    
    Ta kombinacja itertools.chaini itertools.repeatstworzy iterator, który da ciągi "Enter a number: "raz i "Not a number! Try again: "nieskończoną liczbę razy:
    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)- tutaj mapzastosuje wszystkie promptsciągi z poprzedniego kroku do inputfunkcji. Na przykład:
    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. Używamy filteri, str.isdigitaby odfiltrować te ciągi, które zawierają tylko cyfry:
    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...
    
    Aby otrzymać tylko pierwszy ciąg zawierający tylko cyfry, którego używamy next.

Inne zasady walidacji:

  1. Metody łańcuchowe: Oczywiście możesz użyć innych metod łańcuchowych, takich jak str.isalphapobieranie tylko ciągów alfabetu lub str.isuppertylko wielkich liter. Pełną listę znajdziesz w dokumentacji .

  2. Testowanie członkostwa: można
    to przeprowadzić na kilka różnych sposobów. Jednym z nich jest użycie __contains__metody:

    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. Porównanie liczb:
    Istnieją przydatne metody porównywania, których możemy tutaj użyć. Na przykład dla __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
    

    Lub, jeśli nie lubisz używać metod dunder (dunder = double-underscore), zawsze możesz zdefiniować własną funkcję lub użyć tych z operatormodułu.

  4. Istnienie ścieżki:
    Tutaj można skorzystać z pathlibbiblioteki i jej Path.existsmetody:

    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
    

Ograniczenie liczby prób:

Jeśli nie chcesz torturować użytkownika, pytając go o coś nieskończoną liczbę razy, możesz określić limit w wezwaniu itertools.repeat. Można to połączyć z podaniem domyślnej wartości nextfunkcji:

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!

Wstępne przetwarzanie danych wejściowych:

Czasami nie chcemy odrzucać danych wejściowych, jeśli użytkownik przypadkowo podał je WIELKIMI LITERAMI lub spacją na początku lub na końcu łańcucha. Aby wziąć pod uwagę te proste błędy, możemy wstępnie przetworzyć dane wejściowe, stosując metody str.loweri str.stripmetody. Na przykład w przypadku testowania członkostwa kod będzie wyglądał następująco:

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

W przypadku, gdy masz wiele funkcji do wykorzystania do przetwarzania wstępnego, łatwiejsze może być użycie funkcji wykonującej kompozycję funkcji . Na przykład używając tego stąd :

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

Łączenie reguł walidacji:

W prostym przypadku, na przykład, gdy program pyta o wiek od 1 do 120 lat, można po prostu dodać kolejny 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)

Ale w przypadku, gdy istnieje wiele reguł, lepiej zaimplementować funkcję wykonującą koniunkcję logiczną . W poniższym przykładzie użyję gotowego stąd :

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

Niestety, jeśli ktoś potrzebuje niestandardowego komunikatu dla każdej nieudanej sprawy, to obawiam się, że nie ma całkiem funkcjonalnego sposobu. A przynajmniej nie mogłem go znaleźć.

13 cat Jan 31 2016 at 10:47

Tak więc ostatnio bawiłem się czymś podobnym do tego i wymyśliłem następujące rozwiązanie, które wykorzystuje sposób uzyskiwania danych wejściowych, które odrzucają śmieci, zanim zostaną nawet sprawdzone w logiczny sposób.

read_single_keypress()dzięki uprzejmości 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()

Kompletny moduł można znaleźć tutaj .

Przykład:

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

Zauważ, że natura tej implementacji polega na tym, że zamyka stdin, gdy tylko zostanie odczytana rzecz, która nie jest cyfrą. Nie nacisnąłem enter po a, ale musiałem po liczbach.

Możesz połączyć to z thismany()funkcją w tym samym module, aby zezwolić tylko na, powiedzmy, trzy cyfry.

12 Georgy May 11 2019 at 03:17

Za pomocą Click :

Click to biblioteka interfejsów wiersza poleceń, która zapewnia funkcjonalność zadawania użytkownikowi prawidłowej odpowiedzi.

Prosty przykład:

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

Zwróć uwagę, jak automatycznie przekonwertował wartość ciągu na liczbę zmiennoprzecinkową.

Sprawdzanie, czy wartość mieści się w zakresie:

Dostępne są różne typy niestandardowe . Aby uzyskać liczbę z określonego zakresu, możemy użyć 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

Możemy też określić tylko jedno z ograniczeń minlub 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

Testowanie członkostwa:

Używanie click.Choicetypu. Domyślnie to sprawdzenie rozróżnia wielkość liter.

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

Praca ze ścieżkami i plikami:

Używając click.Pathtypu, możemy sprawdzić istniejące ścieżki, a także je rozwiązać:

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

Czytanie i zapisywanie plików można wykonać przez 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!

Inne przykłady:

Potwierdzenie hasła:

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

Wartości domyślne:

W takim przypadku po prostu naciśnięcie Enter(lub dowolnego klawisza, którego używasz) bez wprowadzania wartości, da ci domyślną:

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 ojasmohril Jun 23 2016 at 17:34
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 JoãoManuelRodrigues Nov 28 2018 at 21:52

Opierając się na doskonałych sugestiach Daniela Q i Patricka Artnera, oto jeszcze bardziej ogólne rozwiązanie.

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

Zdecydowałem się na jawne ifi raiseinstrukcje zamiast a assert, ponieważ sprawdzanie asercji może być wyłączone, podczas gdy walidacja powinna być zawsze włączona, aby zapewnić niezawodność.

Można to wykorzystać do uzyskania różnych rodzajów danych wejściowych, z różnymi warunkami walidacji. Na przykład:

# 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!"})

Lub, aby odpowiedzieć na pierwotne pytanie:

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 PratikAnand Apr 30 2017 at 16:29

Spróbuj tego:-

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')
1 np8 Oct 30 2020 at 14:12

Tak, spóźniłem się 6 lat od 🎉, ale to pytanie zasługuje na bardziej aktualną odpowiedź.

Rozdzielenie obaw

Jestem wielkim fanem filozofii Uniksa „Zrób jedną rzecz i zrób to dobrze” . W tego typu problemach lepiej jest podzielić problem

  • Pytaj, get_inputaż wejście będzie w porządku.
  • Sprawdź poprawność w validatorfunkcji. Możesz napisać różne walidatory dla różnych zapytań wejściowych.

Prośba o wkład

Może być tak prosty, jak (Python 3+)

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

def get_input(prompt, validator, on_validationerror):
    while True:
        value = input(prompt)
        if validator(value):
            return value
        print(on_validationerror)

Przykład

In [2]: get_input('Give a positive number: ', myvalidator, 'Please, try again')
Give a positive number: foobar
Please, try again
Give a positive number: -10
Please, try again
Give a positive number: 42
Out[2]: '42'

Python 3.8+ Uwaga

W Pythonie 3.8+ możesz użyć operatora morsa

def get_input(prompt, validator, on_validationerror):
    while not validator(value := input(prompt)):
        print(on_validationerror)
    return value 
2Cubed May 31 2016 at 03:47

Chociaż try/ exceptblok będzie działał, znacznie szybszym i czystszym sposobem wykonania tego zadania byłoby użycie 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.")
SiddharthSatpathy Dec 18 2018 at 13:17

Dobre pytanie! W tym celu możesz wypróbować następujący kod. =)

Ten kod używa ast.literal_eval () do znalezienia typu danych input ( age). Następnie postępuje zgodnie z następującym algorytmem:

  1. Poproś użytkownika o wprowadzenie jej age.

    1.1. Jeśli agejest floatlub inttyp danych:

    • Sprawdź, czy age>=18. Jeśli age>=18, wydrukuj odpowiednie wyjście i zakończ.

    • Sprawdź, czy 0<age<18. Jeśli 0<age<18, wydrukuj odpowiednie wyjście i zakończ.

    • Jeśli age<=0poproś użytkownika o ponowne wprowadzenie prawidłowej liczby określającej wiek ( tj. Wróć do kroku 1.)

    1.2. Jeśli agenie floatlub inttyp danych, poproś użytkownika o ponowne wprowadzenie wieku ( tj. Wróć do kroku 1).

Oto kod.

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.") 
Ep1c1aN Jul 01 2019 at 16:36

Zawsze możesz zastosować prostą logikę if-else i dodać jeszcze jedną iflogikę do kodu wraz z forpętlą.

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

To będzie nieskończona toaleta i zostaniesz poproszony o wejście w ten wiek na czas nieokreślony.

Liju Aug 17 2020 at 15:24

Poniższy kod może pomóc.

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

Jeśli chcesz mieć maksymalną liczbę prób, powiedzmy 3, użyj poniższego kodu

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

Uwaga: używa rekurencji.

behnaz.sheikhi Sep 09 2020 at 19:30

Użyj try-except, aby obsłużyć błąd i powtórz go ponownie:

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