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?

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!


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”


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.


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.


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źć.


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

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

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

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

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

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

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.


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.

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”.


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

-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

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

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

-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


„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


To również nie odpowiada na pytanie.
— Georgy
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.