Pytanie użytkownika o dane wejściowe, dopóki nie udzieli prawidłowej odpowiedzi

562

Piszę program, który akceptuje 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 nie powiedzie się, 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 awarii chciałbym, aby program ponownie poprosił o dane wejściowe. 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 zmusić program do żądania poprawnych danych zamiast zawieszania się po wprowadzeniu nie sensownych danych?

Jak mogę odrzucić takie wartości -1, które intw tym kontekście są prawidłowe , ale bezsensowne?

Kevin
źródło

Odpowiedzi:

703

Najprostszym sposobem na osiągnięcie tego jest umieszczenie inputmetody w pętli while. Użyj, continuegdy dostaniesz złe dane wejściowe, i breakwyjdź z pętli, gdy będziesz zadowolony.

Kiedy Twoje dane wejściowe mogą 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 zasad walidacji

Jeśli chcesz odrzucić wartości, które Python może z powodzeniem parsować, możesz dodać własną logikę sprawdzania poprawności.

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

Łącząc obsługę wyjątków i niestandardową weryfikację

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 wszystkiego w funkcji

Jeśli musisz poprosić użytkownika o wiele różnych wartości, przydatne może być wstawienie tego kodu do funkcji, abyś nie musiał 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: ")

Kładąc wszystko razem

Możesz rozszerzyć ten pomysł, aby stworzyć bardzo ogólną funkcję wprowadzania:

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 jest ogólnie 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ótszy niż while Truemetoda, ale narusza zasadę tworzenia oprogramowania „ Nie powtarzaj się ”. Zwiększa to prawdopodobieństwo błędów w twoim systemie. Co jeśli chcesz cofnąć się do wersji 2.7, zmieniając inputna raw_input, ale przypadkowo zmień tylko pierwszą z inputpowyższych? To jestSyntaxError tylko czekanie na wydarzenie.

Rekurencja rozwali Twój stos

Jeśli właśnie dowiedziałeś się o rekurencji, możesz mieć pokusę, aby użyć jej w get_non_negative_intcelu pozbycia 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 się za pomocą RuntimeError: maximum recursion depth exceeded. Możesz myś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!

Kevin
źródło
53
Fajnie jest czytać go z wieloma przykładami, hołd. Niedoceniana lekcja: „Nie lekceważ pomysłowości głupców!”
vpibano
3
Zresztą i tak nie tylko głosowałbym na pytania i odpowiedzi, ponieważ są świetne, ale podpisałeś umowę z „dickety six”. Dobra robota, @Kevin.
erekalper
1
Nie oceniaj pomysłowości głupców ... i sprytnych napastników. Atak DOS byłby najłatwiejszy do tego rodzaju rzeczy, ale inne mogą być możliwe.
Solomon Ucko
Czy możemy użyć nowego operatora „morsa” zamiast zbędnych danych wejściowych? Czy to też zły styl?
J Arun Mani
1
@JArunMani Nie sądzę, że byłby to zły styl, ale może być trochę mniej czytelny. Rzeczywiście będziesz mieć tylko jedną inputna pętlę i pętla stanie się bardzo krótka, ale stan może stać się dość długi ...
Tomerikoo
39

Dlaczego miałbyś zrobić while Truea następnie zerwać z tej pętli, podczas gdy możesz po prostu umieścić swoje wymagania w instrukcji while, ponieważ 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.")

Spowodował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.

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

Steven Stip
źródło
22

Chociaż przyjęta odpowiedź jest niesamowita. Chciałbym również udostępnić szybki hack dla 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 dotyczy Pythona 3.x.

aaveg
źródło
1
Zauważ, że ten kod jest rekurencyjny, ale rekurencja nie jest tutaj potrzebna i, jak powiedział Kevin, może zdmuchnąć twój stos.
PM 2,
2
@ PM2Ring - masz rację. Ale moim celem tutaj było tylko pokazanie, w jaki sposób „zwarcie” może zminimalizować (upiększyć) długie fragmenty kodu.
aaveg
11
Dlaczego miałbyś przypisywać lambda do zmiennej, po prostu użyj defzamiast tego. def f(age):jest znacznie jaśniejszy niżf = lambda age:
GP89
3
W niektórych przypadkach wiek może być potrzebny tylko raz, a następnie nie można użyć tej funkcji. Można chcieć użyć funkcji i wyrzucić ją po zakończeniu pracy. Może to nie być najlepszy sposób, ale zdecydowanie jest to inny sposób (co było celem mojego rozwiązania).
aaveg
@aaveg, jak byś zmienił ten kod, aby faktycznie zapisać wiek podany przez użytkownika?
Tytire Recubans
12

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, w jaki otrzymujesz dane wejściowe, które odrzucają śmieci, zanim zostaną nawet sprawdzone w jakikolwiek 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 naturą tej implementacji jest to, że zamyka ona standardowe wejście, gdy tylko zostanie przeczytane coś, co nie jest cyfrą. Później nie nacisnąłem Entera , ale musiałem po liczbach.

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

kat
źródło
12

Funkcjonalne podejście lub „ look mama 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 „niepoprawne dane wejściowe” był oddzielony od monitu o wprowadzenie, 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.repeatutworzy 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.isdigitdo filtrowania ciągów zawierających 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...
    I aby uzyskać tylko pierwsze ciągi zawierające tylko cyfry, których używamy next.

Inne zasady sprawdzania poprawności:

  1. Metody ciągów: Oczywiście możesz użyć innych metod ciągów, takich jak str.isalphauzyskiwanie tylko ciągów alfabetycznych lub str.isuppertylko wielkich liter. Widzieć docs dla pełnej listy.

  2. Testowanie członkostwa:
    Istnieje kilka różnych sposobów jego wykonania. 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 użyć tutaj. 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 dundera (dunder = double-undercore), zawsze możesz zdefiniować własną funkcję lub użyć funkcji z operatormodułu.

  4. Istnienie ścieżki:
    Tutaj można użyć 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

Ograniczająca liczba prób:

Jeśli nie chcesz torturować użytkownika, zadając mu nieskończoną liczbę pytań, możesz określić limit w wywołaniu itertools.repeat. Można to połączyć z dostarczeniem wartości domyślnej dla 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 dostarczył je W CAPSACH lub ze 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 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 wstępnego przetwarzania, łatwiejsze może być użycie funkcji wykonującej kompozycję funkcji . Na przykład, korzystając z tego tutaj :

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ł sprawdzania poprawności:

W prostym przypadku, na przykład, gdy program prosi o wiek od 1 do 120 lat, można po prostu dodać inny 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ą logiczną koniunkcję . 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, obawiam się, że nie ma całkiem funkcjonalnego sposobu. A przynajmniej nie mogłem znaleźć.

Georgy
źródło
Cóż za dokładna i wspaniała odpowiedź, podział wyjaśnień był wspaniały.
Locane
Używając swojego stylu, w jaki sposób można przejść do usuwania białych znaków i zmniejszania nakładów na testy członkostwa? Nie chcę tworzyć zestawu, który musi zawierać zarówno duże, jak i małe przykłady. Chciałbym również pozwolić na błędy wprowadzania białych znaków.
Austin
1
@Austin Dodałem nową sekcję dotyczącą przetwarzania wstępnego. Spójrz.
Georgy,
To przypomina mi ReactiveX. Ale może inspiracją były przede wszystkim języki funkcjonalne?
Mateen Ulhaq
8

Za pomocą Click :

Click jest biblioteką dla interfejsów wiersza poleceń i zapewnia funkcjonalność umożliwiającą uzyskanie prawidłowej odpowiedzi od użytkownika.

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

Zauważ, 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 również określić tylko jeden z limitów 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 w tym sprawdzeniu rozróżniana jest 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:

Za pomocą 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

Odczytywanie i zapisywanie plików można wykonać poprzez 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 wystarczy nacisnąć Enter(lub jakikolwiek używany klawisz) bez wprowadzania wartości, aby uzyskać domyślny:

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
Georgy
źródło
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."
ojas mohril
źródło
2

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 wyraźne ifi raiseoświadczenia zamiastassert , ponieważ sprawdzanie asercji może być wyłączone, podczas gdy sprawdzanie poprawności powinno być zawsze włączone, aby zapewnić niezawodność.

Może to być wykorzystane 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 oryginalne 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.")
João Manuel Rodrigues
źródło
1

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')
Pratik Anand
źródło
0

Podczas gdy blok try/ exceptbędzie działał, znacznie szybszym i czystszym sposobem na wykonanie 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.")
Kostka
źródło
0

Dobre pytanie! Możesz wypróbować następujący kod. =)

Ten kod używa ast.literal_eval (), aby znaleźć typ danych input ( age). Następnie postępuje według następującego algorytmu:

  1. Poproś użytkownika o wpisanie jej / jego age.

    1.1 Jeśli agejest floatlub inttyp danych:

    • Sprawdź, czy age>=18. Jeśli age>=18, wydrukuj odpowiednie dane wyjściowe i wyjdź.

    • Sprawdź, czy 0<age<18. Jeśli 0<age<18, wydrukuj odpowiednie dane wyjściowe i wyjdź.

    • Jeśli age<=0poproś użytkownika o ponowne wprowadzenie prawidłowego numeru wieku ( tj. Wróć do kroku 1)

    1.2 Jeśli agenie jest floatlub inttyp danych, poproś użytkownika o ponowne wprowadzenie swojego 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.") 
Siddharth Satpathy
źródło
0

Zawsze możesz zastosować prostą logikę if-else i dodać jeszcze jedną iflogikę do swojego 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ńczony kibel i zostaniesz poproszony o wejście w wiek na czas nieokreślony.

rev Ep1c1aN
źródło
To tak naprawdę nie odpowiada na pytanie. Pytanie dotyczyło uzyskania wkładu użytkownika, dopóki nie udzieli prawidłowej odpowiedzi, nie bez końca .
Georgy
-1

Możesz napisać bardziej ogólną logikę, aby umożliwić użytkownikowi wprowadzanie tylko określonej liczby razy, ponieważ taki sam przypadek użycia występuje 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.
Mangu Singh Rajpurohit
źródło
1
zapominasz zwiększyć wartość iCount po każdej pętli
Hoai-Thu Vuong
-1

Możesz wykonać instrukcję wejściową na chwilę True, aby wielokrotnie pytała o dane wejściowe użytkowników, a następnie przerwać tę pętlę, jeśli użytkownik wprowadzi odpowiedź, którą chcesz. I możesz użyć try i oprócz bloków 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 taka, że ​​jeśli użytkownik wprowadzi ciąg zamiast liczby całkowitej, program nie zwróci „Nie można głosować w Stanach Zjednoczonych”.

użytkownik9142415
źródło
-1

Używaj instrukcji „while”, dopóki użytkownik nie wprowadzi prawdziwej wartości, a jeśli wartość wejściowa nie jest liczbą lub jest wartością zerową, pomiń ją i spróbuj ponownie zapytać i tak dalej. Na przykład próbowałem odpowiedzieć naprawdę na twoje pytanie. Jeśli przypuszczamy, że nasz wiek wynosi od 1 do 150, wówczas wartość wejściowa jest akceptowana, w przeciwnym razie jest to zła wartość. Do zakończenia programu użytkownik może użyć klawisza 0 i wprowadzić go jako wartość.

Uwaga: Przeczytaj komentarze na początku 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.")
Saeed Zahedian Abroodi
źródło
-1

Jeszcze jedno rozwiązanie do używania sprawdzania poprawności danych wejściowych przy użyciu niestandardowego ValidationErrori (opcjonalnego) sprawdzania poprawności zakresu dla liczb całkowitych:

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

Stosowanie:

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)

Wynik:

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
Patrick Artner
źródło
-1

Oto czystsze, bardziej uogólnione rozwiązanie, które pozwala uniknąć powtarzających się bloków if / else: napisz funkcję, która bierze pary (Błąd, monit o błąd) w słowniku i wykonuj wszystkie sprawdzenia wartości za pomocą asercji.

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

Stosowanie:

d = {ValueError: 'Integers only', AssertionError: 'Positive numbers only', 
     KeyboardInterrupt: 'You can never leave'}
user_input = validate_input("Positive number: ", d)
Daniel Q
źródło
-1

Stałe wprowadzanie danych przez użytkownika za pomocą funkcji rekurencyjnej :

Strunowy

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

name = askName()

Liczba całkowita

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

age = askAge()

i wreszcie wymóg pytania:

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)
Roko C. Buljan
źródło
-2

Prostym rozwiązaniem byłoby:

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

Wyjaśnienie powyższego kodu: Aby uzyskać prawidłowy wiek, powinien być dodatni i nie powinien być dłuższy niż normalny wiek fizyczny, np. Maksymalny wiek to 120 lat.

Następnie możemy poprosić użytkownika o podanie wieku, a jeśli wartość wieku jest ujemna lub wyższa niż 120, uważamy, że jest to nieprawidłowa wartość i prosi użytkownika o ponowną próbę.

Po wprowadzeniu prawidłowych danych wejściowych sprawdzamy (za pomocą zagnieżdżonej instrukcji if-else), czy wiek wynosi> = 18 lub odwrotnie, i drukujemy komunikat, czy użytkownik jest uprawniony do głosowania

Rohail
źródło
„Podaj swój wiek: dickety six”: ta sama awaria, jak podano w pytaniu ...
BDL
-2

weź dane wejściowe jako ciąg i użyj isdigit (), aby sprawdzić, czy dane wejściowe mają tylko cyfry, a nie puste, nie mogę być

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

Dead Pool
źródło
To również nie odpowiada na pytanie.
Georgy