Proszenie użytkownika o wprowadzenie, dopóki nie udzieli prawidłowej odpowiedzi

Piszę program, który musi akceptować 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.")

Działa to zgodnie z oczekiwaniami, jeśli użytkownik wprowadzi sensowne dane.

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

Ale jeśli popełnią błąd, to się rozwali:

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 upaść, chciałbym, aby spróbował ponownie uzyskać dane wejściowe. Tak:

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 mogę tego dokonać? Co by było, gdybym również chciał odrzucić wartości takie jak -1, które są poprawne int, ale bezsensowne w tym kontekście?

Author: Kevin, 2014-04-25

13 answers

Najprostszym sposobem, aby to osiągnąć, byłoby umieszczenie metody input W pętli while. Użycie continue gdy masz złe dane wejściowe, i break z pętli, gdy jesteś zadowolony.

Kiedy Twoje dane mogą wywołać wyjątek

Użyj spróbuj złapać , aby wykryć, kiedy użytkownik wprowadzi dane, których nie można przetworzyć.

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

Implementowanie Własnych Zasad Walidacji

Jeśli chcesz odrzucić wartości, które Python może pomyślnie przeanalizować, możesz dodać swoje własna logika 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

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

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

Enkapsulating it All in a Function

Jeśli musisz poprosić Użytkownika o wiele różnych wartości, przydatne może być umieszczenie tego kodu w funkcji, więc nie musisz go ponownie wpisywać za każdym razem.

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

Składanie Wszystkiego Razem

Możesz rozszerzyć ten pomysł, aby wprowadzić bardzo ogólne dane funkcja:

def sanitised_input(prompt, type_=None, min_=None, max_=None, range_=None):
    if min_ is not None and max_ is not None and max_ < min_:
        raise ValueError("min_ must be less than or equal to max_.")
    while True:
        ui = input(prompt)
        if type_ is not None:
            try:
                ui = type_(ui)
            except ValueError:
                print("Input type must be {0}.".format(type_.__name__))
                continue
        if max_ is not None and ui > max_:
            print("Input must be less than or equal to {0}.".format(max_))
        elif min_ is not None and ui < min_:
            print("Input must be greater than or equal to {0}.".format(min_))
        elif range_ is not None and ui not in range_:
            if isinstance(range_, range):
                template = "Input must be between {0.start} and {0.stop}."
                print(template.format(range_))
            else:
                template = "Input must be {0}."
                if len(range_) == 1:
                    print(template.format(*range_))
                else:
                    print(template.format(" or ".join((", ".join(map(str,
                                                                     range_[:-1])),
                                                       str(range_[-1])))))
        else:
            return ui

Z użyciem np.:

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ć

Zbędne użycie zbędnych input oświadczeń

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

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ż metoda while True, ale narusza ona Nie powtarzaj się {21]} zasadę tworzenia oprogramowania. Zwiększa to prawdopodobieństwo wystąpienia błędów w systemie. Co? jeśli chcesz przenieść do 2.7 zmieniając input na raw_input, ale przypadkowo Zmień tylko pierwszą input powyżej? To jest SyntaxError tylko czekać, aby się stało.

Rekurencja Rozwali Twój Stos

Jeśli właśnie nauczyłeś się o rekurencji, możesz pokusić się o użycie jej w get_non_negative_int, aby 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

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

 513
Author: Kevin,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-03-15 17:37:10

Dlaczego miałbyś zrobić while True, a następnie wyrwać się z tej pętli, podczas gdy możesz również po prostu umieścić swoje wymagania w instrukcji while, ponieważ wszystko, co chcesz, to przestać, gdy masz 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.")

Wynikałoby to z:

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 podąża za logiką Twojego "procesu biznesowego"

 26
Author: Steven Stip,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-02-24 16:44:54

Chociaż zaakceptowana odpowiedź jest niesamowita. Chciałbym również podzielić się szybki hack dla tego problemu. (Dotyczy to również problemu negatywnego wieku.)

f=lambda age: (age.isdigit() and ((int(age)>=18  and "Can vote" ) or "Cannot vote")) or \
f(raw_input("invalid input. Try again\nPlease enter your age: "))
print f(raw_input("Please enter your age: "))

P. S. Ten kod jest dla Pythona 2.x i mogą być eksportowane do 3.x zmieniając funkcje raw_input i print.

 15
Author: aaveg,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-06-28 23:29:47

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

read_single_keypress() uprzejmość 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ł znajdziesz 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 jest taka, że zamyka stdin jak tylko coś, co nie jest cyfra jest odczytywana. Nie wcisnąłem enter po a, ale musiałem po numerach.

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

 8
Author: cat,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:34:45
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
Author: ojas mohril,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-06-23 10:34:14

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')
 2
Author: Pratik Anand,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-04-30 09:29:28

Aby edytować kod i naprawić błąd:

while True:
    try:
       age = int(input("Please enter your age: "))
       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.")
           break
    except ValueError:
       print("Please enter a valid response")
 2
Author: ᴡʜᴀᴄᴋᴀᴍᴀᴅᴏᴏᴅʟᴇ3000,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-08-09 16:19:41

Możesz napisać bardziej ogólną logikę, aby umożliwić użytkownikowi wprowadzanie tylko określonej liczby razy, ponieważ ten sam przypadek użycia pojawia się w wielu rzeczywistych aplikacjach.

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
Author: Mangu Singh Rajpurohit,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-11-03 07:49:29

Użyj instrukcji" while " dopóki użytkownik nie wprowadzi wartości true i jeśli wartość wejściowa nie jest liczbą lub jest wartością null, pomiń ją i spróbuj ponownie zapytać itd. W przykładzie starałem się odpowiedzieć naprawdę na twoje pytanie. Jeśli założymy, że nasz wiek wynosi od 1 do 150, to akceptowana jest wartość wejściowa, w przeciwnym razie jest to niewłaściwa wartość. Do zakończenia programu użytkownik może użyć klawisza 0 i wprowadzić go jako wartość.

Uwaga: czytaj komentarze na górze kodu.

# 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
Author: Saeed Zahedian Abroodi,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-07-07 09:28:33

While a try/except block 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.")
 0
Author: 2Cubed,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-06-06 07:15:17

Możesz ustawić instrukcję input jako pętlę while True, aby wielokrotnie pytała o Dane wejściowe użytkowników, a następnie przerwała tę pętlę, jeśli użytkownik wprowadzi żądaną odpowiedź. I możesz użyć bloków try i Exception do obsługi nieprawidłowych odpowiedzi.

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

Zmienna var jest tak, że jeśli użytkownik wprowadzi ciąg znaków zamiast liczby całkowitej, program nie zwróci " nie jesteś w stanie głosować w Stanach Zjednoczonych."

 0
Author: user9142415,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-01-03 00:59:37

Użyj try catch z pętlą never ending while. Aby sprawdzić, czy pusty łańcuch jest pusty, użyj instrukcji IF.

while True:
    name = input("Enter Your Name\n")
    if not name:
        print("I did not understood that")
        continue
    else:
        break

while True:
    try:
        salary = float(input("whats ur salary\n"))
    except ValueError:
        print("I did not understood that")
        continue
    else:
        break

while True:
    try:
        print("whats ur age?")
        age = int(float(input()))
    except ValueError:
        print("I did not understood that")
        continue
    else:
        break

print("Hello "+ name +  "\nYour salary is " + str(salary) + '\nand you will be ' + str(age+1) +' in a Year')
 0
Author: Mahesh Sonavane,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-07-04 13:49:48

To będzie nadal prosić użytkownika o wprowadzenie numeru, dopóki nie wprowadzi poprawnego numeru:

#note: Python 2.7 users should use raw_input, the equivalent of 3.X's input
while(1):
    try:
        age = int(input("Please enter your age: "))
        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.")
            break()
    except:
        print("Please only enter numbers ")
 0
Author: Ray Hu,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-09-15 06:25:42