Chiedere all'utente l'input fino a quando non danno una risposta valida


562

Sto scrivendo un programma che accetta un input da parte dell'utente.

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

Il programma funziona come previsto fintanto che l'utente immette dati significativi.

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

Ma non riesce se l'utente immette dati non validi:

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'

Invece di andare in crash, vorrei che il programma chiedesse di nuovo l'input. Come questo:

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!

Come posso fare in modo che il programma richieda input validi invece di arresti anomali quando vengono immessi dati non sensibili?

Come posso rifiutare valori come -1, che è valido int, ma senza senso in questo contesto?

Risposte:


704

Il modo più semplice per ottenere ciò è mettere il inputmetodo in un ciclo while. Utilizzare continuequando si ottiene un input errato e breakfuori dal ciclo quando si è soddisfatti.

Quando il tuo input potrebbe sollevare un'eccezione

Utilizzare tryeexcept per rilevare quando l'utente immette dati che non possono essere analizzati.

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

Implementazione delle proprie regole di convalida

Se si desidera rifiutare i valori che Python può analizzare correttamente, è possibile aggiungere la propria logica di convalida.

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

Combinazione di gestione delle eccezioni e convalida personalizzata

Entrambe le tecniche di cui sopra possono essere combinate in un loop.

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

Incapsulare tutto in una funzione

Se devi chiedere al tuo utente molti valori diversi, potrebbe essere utile inserire questo codice in una funzione, quindi non è necessario ridigitarlo ogni volta.

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

Mettere tutto insieme

Puoi estendere questa idea per creare una funzione di input molto generica:

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

Con utilizzo come:

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

Insidie ​​comuni e perché dovresti evitarle

L'uso ridondante delle inputdichiarazioni ridondanti

Questo metodo funziona ma è generalmente considerato stile scadente:

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

All'inizio potrebbe sembrare attraente perché è più breve del while Truemetodo, ma viola il principio di non ripetersi dello sviluppo del software. Ciò aumenta la probabilità di bug nel sistema. Cosa succede se si desidera eseguire il backport su 2.7 cambiando inputin raw_input, ma cambiando accidentalmente solo il primo inputsopra? È SyntaxErrorsolo un'attesa che accada.

La ricorsione farà esplodere il tuo stack

Se hai appena imparato a conoscere la ricorsione, potresti essere tentato di usarlo in get_non_negative_intmodo da poter eliminare il ciclo 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

Questo sembra funzionare bene per la maggior parte del tempo, ma se l'utente immette dati non validi abbastanza volte, lo script terminerà con un RuntimeError: maximum recursion depth exceeded. Potresti pensare "nessun pazzo farebbe 1000 errori di fila", ma stai sottovalutando l'ingegnosità dei pazzi!


53
È divertente leggerlo con molti esempi, complimenti. Lezione sottovalutata: "Non sottovalutare l'ingegnosità degli sciocchi!"
vpibano,

3
Non solo avrei votato entrambe le domande e risposte, in quanto sono fantastiche, ma hai anche siglato l'accordo con "dickety six". Ben fatto, @Kevin.
erekalper,

1
Non stimare l'ingegnosità degli sciocchi ... e degli aggressori intelligenti. Un attacco DOS sarebbe più semplice per questo genere di cose, ma altri potrebbero essere possibili.
Solomon Ucko,

Possiamo usare il nuovo operatore "tricheco" invece di input ridondanti? È anche uno stile povero?
J Arun Mani,

1
@JArunMani Non penso che sarebbe uno stile scadente, ma potrebbe essere un po 'meno leggibile. Avrai davvero solo uno inputper loop e il loop diventerà molto breve, ma la condizione potrebbe diventare piuttosto lunga ...
Tomerikoo

39

Perché dovresti fare un while Truee poi uscire da questo ciclo mentre puoi anche inserire i tuoi requisiti nell'istruzione while poiché tutto ciò che vuoi è fermarti una volta che hai l'età?

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

Ciò comporterebbe quanto segue:

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.

questo funzionerà fin dall'età non avrà mai un valore che non avrà senso e il codice segue la logica del tuo "processo aziendale"


22

Sebbene la risposta accettata sia sorprendente. Vorrei anche condividere un trucco rapido per questo problema. (Questo si occupa anche del problema dell'età negativa.)

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 Questo codice è per Python 3.x.


1
Nota che questo codice è ricorsivo, ma la ricorsione non è necessaria qui, e come ha detto Kevin, può far esplodere il tuo stack.
PM 2Ring

2
@ PM2Ring - hai ragione. Ma il mio scopo qui era solo quello di mostrare come il "corto circuito" può minimizzare (abbellire) pezzi di codice lunghi.
aaveg

11
Perché dovresti assegnare un lambda a una variabile, basta usare definvece. def f(age):è molto più chiaro dif = lambda age:
GP89,

3
In alcuni casi, potresti aver bisogno dell'età solo una volta e quindi non è possibile utilizzare questa funzione. Uno potrebbe voler usare una funzione e gettarla via dopo che il lavoro è finito. Inoltre, questo potrebbe non essere il modo migliore, ma è sicuramente un modo diverso di farlo (che era lo scopo della mia soluzione).
aaveg

@aaveg come trasformeresti questo codice per salvare effettivamente l'età fornita dall'utente?
Tytire Recubans,

12

Quindi, stavo scherzando con qualcosa di simile a questo di recente, e mi è venuta in mente la seguente soluzione, che utilizza un modo per ottenere input che rifiuta la spazzatura, prima ancora che venga verificata in modo logico.

read_single_keypress()per gentile concessione 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()

Puoi trovare il modulo completo qui .

Esempio:

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

Si noti che la natura di questa implementazione è che chiude stdin non appena viene letto qualcosa che non è una cifra. Non ho premuto Invio dopo a, ma dovevo cercare i numeri.

Puoi unire questo con la thismany()funzione nello stesso modulo per consentire, per esempio, solo tre cifre.


12

Approccio funzionale o " guarda mamma senza loop! ":

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

o se si desidera avere un messaggio "input errato" separato da un prompt di input come in altre risposte:

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

Come funziona?

  1. prompts = chain(["Enter a number: "], repeat("Not a number! Try again: "))
    Questa combinazione di itertools.chaine itertools.repeatcreerà un iteratore che produrrà stringhe "Enter a number: "una volta e "Not a number! Try again: "un numero infinito di volte:
    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)- qui mapverranno applicate tutte le promptsstringhe del passaggio precedente alla inputfunzione. Per esempio:
    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. Usiamo filtere str.isdigitfiltriamo quelle stringhe che contengono solo cifre:
    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...
    E per ottenere solo la prima stringa di sole cifre che usiamo next.

Altre regole di convalida:

  1. Metodi di stringa: puoi ovviamente usare altri metodi di stringa come str.isalphaottenere solo stringhe alfabetiche o solo lettere str.isuppermaiuscole. Consulta i documenti per l'elenco completo.

  2. Test di appartenenza:
    esistono diversi modi per eseguirlo. Uno di questi è utilizzando il __contains__metodo:

    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. Confronto di numeri:
    ci sono metodi di confronto utili che possiamo usare qui. Ad esempio, per __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

    Oppure, se non ti piace usare i metodi dunder (dunder = double-underscore), puoi sempre definire la tua funzione o usare quelli del operatormodulo.

  4. Esistenza del percorso:
    qui è possibile utilizzare la pathliblibreria e il suo Path.existsmetodo:

    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

Numero limite di tentativi:

Se non vuoi torturare un utente chiedendogli qualcosa un numero infinito di volte, puoi specificare un limite in una chiamata di itertools.repeat. Questo può essere combinato con la fornitura di un valore predefinito alla nextfunzione:

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!

Preelaborazione dei dati di input:

A volte non vogliamo rifiutare un input se l'utente lo fornisce accidentalmente IN MAIUSCOLO o con uno spazio all'inizio o alla fine della stringa. Per tenere conto di questi semplici errori, possiamo preelaborare i dati di input applicando str.lowere str.stripmetodi. Ad esempio, nel caso di test di appartenenza il codice sarà simile al seguente:

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

Nel caso in cui si disponga di molte funzioni da utilizzare per la preelaborazione, potrebbe essere più semplice utilizzare una funzione che esegue una composizione di funzioni . Ad esempio, usando quello da qui :

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

Combinazione di regole di convalida:

Per un semplice caso, ad esempio, quando il programma richiede un'età compresa tra 1 e 120 anni, si può semplicemente aggiungere un altro 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)

Ma nel caso in cui ci siano molte regole, è meglio implementare una funzione che esegue una congiunzione logica . Nel seguente esempio ne userò uno pronto da qui :

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

Sfortunatamente, se qualcuno ha bisogno di un messaggio personalizzato per ogni caso fallito, quindi, temo, non esiste un modo abbastanza funzionale. O almeno non riuscivo a trovarne uno.


Che risposta completa e meravigliosa, la spiegazione della spiegazione è stata fantastica.
Locane,

Usando il tuo stile, come si farebbe per eliminare gli spazi bianchi e ridurre l'input per i test di appartenenza? Non voglio creare un set che deve includere esempi sia maiuscoli che minuscoli. Vorrei anche consentire errori di input di spazi bianchi.
Austin,

1
@Austin Ho aggiunto una nuova sezione sulla preelaborazione. Guarda.
Georgy,

Questo mi ricorda ReactiveX. Ma forse questo è stato ispirato dai linguaggi funzionali in primo luogo?
Mateen Ulhaq,

8

Utilizzando Click :

Click è una libreria per interfacce da riga di comando e fornisce funzionalità per chiedere una risposta valida da un utente.

Esempio semplice:

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

Nota come ha convertito automaticamente il valore di stringa in un float.

Verifica se un valore rientra in un intervallo:

Sono disponibili diversi tipi personalizzati . Per ottenere un numero in un intervallo specifico possiamo usare 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

Possiamo anche specificare solo uno dei limiti, minoppure 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

Test di appartenenza:

Usando il click.Choicetipo. Per impostazione predefinita, questo controllo fa distinzione tra maiuscole e minuscole.

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

Lavorare con percorsi e file:

Usando un click.Pathtipo possiamo verificare i percorsi esistenti e risolverli anche:

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

La lettura e la scrittura dei file può essere effettuata tramite 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!

Altri esempi:

Conferma password:

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

Valori standard:

In questo caso, semplicemente premendo Enter(o qualunque tasto tu usi) senza inserire un valore, ne otterrai uno predefinito:

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

Basandosi sugli eccellenti suggerimenti di Daniel Q e Patrick Artner, ecco una soluzione ancora più generalizzata.

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

Ho optato per esplicite ife raisele dichiarazioni invece di una assert, perché il controllo affermazione potrebbe essere spento, mentre la convalida deve essere sempre a fornire robustezza.

Questo può essere usato per ottenere diversi tipi di input, con diverse condizioni di validazione. Per esempio:

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

Oppure, per rispondere alla domanda originale:

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

Prova questo:-

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

Mentre un blocco try/ exceptfunzionerà, sarebbe un modo molto più veloce e più pulito per realizzare questo compito 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

Buona domanda! Puoi provare il seguente codice per questo. =)

Questo codice utilizza ast.literal_eval () per trovare il tipo di dati dell'input ( age). Quindi segue il seguente algoritmo:

  1. Chiedi all'utente di inserire il suo / suo age.

    1.1. Se ageè floato inttipo di dati:

    • Controlla se age>=18. Se age>=18, stampare l'output appropriato ed uscire.

    • Controlla se 0<age<18. Se 0<age<18, stampare l'output appropriato ed uscire.

    • Se age<=0, chiedi all'utente di inserire di nuovo un numero valido per l'età, ( es. Torna al passaggio 1.)

    1.2. In caso agecontrario floato inttipo di dati, chiedi all'utente di inserire nuovamente la sua età (ad es. Torna al passaggio 1)

Ecco il codice

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

Puoi sempre applicare una semplice logica if-else e aggiungere un'altra iflogica al tuo codice insieme a un forciclo.

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

Questo sarà un gabinetto infinito e ti verrà chiesto di entrare nell'età, indefinitamente.


Questo non risponde davvero alla domanda. La domanda era di ottenere l'input di un utente fino a quando non avesse dato una risposta valida, non indefinitamente .
Georgy,

-1

È possibile scrivere una logica più generale per consentire all'utente di immettere solo un numero specifico di volte, poiché lo stesso caso d'uso si presenta in molte applicazioni del mondo reale.

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
dimentichi di aumentare il valore iCount dopo ogni ciclo
Hoai-Thu Vuong

-1

Puoi fare l'istruzione input per un po 'True loop in modo che richieda ripetutamente l'input dell'utente e quindi interrompere quel loop se l'utente inserisce la risposta che desideri. E puoi usare try e tranne i blocchi per gestire le risposte non valide.

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

La variabile var è tale che se l'utente inserisce una stringa anziché un numero intero il programma non restituirà "Non è possibile votare negli Stati Uniti".


-1

Utilizzare l'istruzione "while" fino a quando l'utente non immette un valore vero e se il valore di input non è un numero o è un valore nullo, ignorarlo e provare a chiedere di nuovo e così via. Nell'esempio ho cercato di rispondere veramente alla tua domanda. Se supponiamo che la nostra età sia compresa tra 1 e 150, il valore di input accettato, altrimenti è un valore errato. Per terminare il programma, l'utente può usare il tasto 0 e inserirlo come valore.

Nota: leggi i commenti all'inizio del codice.

# 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

Un'altra soluzione per l'utilizzo della convalida dell'input utilizzando una ValidationErrorconvalida dell'intervallo personalizzata e (facoltativa) per gli input interi:

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

Uso:

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)

Produzione:

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

Ecco una soluzione più pulita e più generalizzata che evita blocchi if / else ripetitivi: scrivi una funzione che accetta coppie (Error, prompt di errore) in un dizionario e fai tutto il controllo del valore con asserzioni.

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

Uso:

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

-1

Input dell'utente persistente mediante la funzione ricorsiva :

Corda

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

name = askName()

Numero intero

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

age = askAge()

e, infine, il requisito della domanda:

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

La soluzione semplice sarebbe:

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

Spiegazione del codice precedente: per un'età valida, dovrebbe essere positiva e non dovrebbe essere superiore alla normale età fisica, ad esempio l'età massima è 120.

Quindi possiamo chiedere all'utente l'età e se l'input dell'età è negativo o superiore a 120, lo consideriamo un input non valido e chiediamo all'utente di riprovare.

Dopo aver inserito l'input valido, eseguiamo un controllo (utilizzando l'istruzione if-else nidificata) se l'età è> = 18 o viceversa e stampiamo un messaggio se l'utente è idoneo a votare


"Per favore, inserisci la tua età: dickety six" ': stesso incidente come indicato nella domanda ...
BDL

-2

accetta input come stringa e usa isdigit () per verificare che l'input abbia solo cifre, non vuote, non può essere -ve

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

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


Inoltre non risponde alla domanda.
Georgy,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.