Demander à l'utilisateur une entrée jusqu'à ce qu'il donne une réponse valide

563

J'écris un programme qui accepte une entrée de l'utilisateur.

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

Le programme fonctionne comme prévu tant que l'utilisateur saisit des données significatives.

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

Mais il échoue si l'utilisateur entre des données invalides:

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'

Au lieu de planter, j'aimerais que le programme demande à nouveau l'entrée. Comme ça:

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!

Comment puis-je faire en sorte que le programme demande des entrées valides au lieu de planter lorsque des données non sensibles sont entrées?

Comment puis-je rejeter des valeurs comme -1, qui est valide int, mais absurde dans ce contexte?

Kevin
la source

Réponses:

705

La façon la plus simple d'y parvenir est de mettre la inputméthode dans une boucle while. À utiliser continuelorsque vous obtenez une mauvaise entrée et breakhors de la boucle lorsque vous êtes satisfait.

Quand votre contribution pourrait soulever une exception

Utilisez tryetexcept pour détecter quand l'utilisateur entre des données qui ne peuvent pas être analysées.

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

Implémentation de vos propres règles de validation

Si vous souhaitez rejeter les valeurs que Python peut analyser avec succès, vous pouvez ajouter votre propre logique de validation.

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

Combinaison de la gestion des exceptions et de la validation personnalisée

Les deux techniques ci-dessus peuvent être combinées en une seule boucle.

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

Tout encapsuler dans une fonction

Si vous devez demander à votre utilisateur un grand nombre de valeurs différentes, il peut être utile de mettre ce code dans une fonction, afin que vous n'ayez pas à le retaper à chaque fois.

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

Mettre tous ensemble

Vous pouvez étendre cette idée pour créer une fonction d'entrée très générique:

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

Avec une utilisation telle que:

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

Pièges courants et pourquoi les éviter

L'utilisation redondante des inputinstructions redondantes

Cette méthode fonctionne mais est généralement considérée comme un style médiocre:

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

Il peut sembler attrayant au départ parce qu'il est plus court que la while Trueméthode, mais il viole le principe de développement logiciel Ne pas répéter . Cela augmente la probabilité de bogues dans votre système. Que se passe-t-il si vous souhaitez rétroporter vers la version 2.7 en passant inputà raw_input, mais modifier accidentellement uniquement la première inputci-dessus? C'est SyntaxErrorjuste une attente pour arriver.

La récursion va exploser votre pile

Si vous venez d'apprendre la récursivité, vous pourriez être tenté de l'utiliser get_non_negative_intpour pouvoir disposer de la boucle 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

Cela semble fonctionner correctement la plupart du temps, mais si l'utilisateur saisit suffisamment de données non valides, le script se terminera par un RuntimeError: maximum recursion depth exceeded. Vous pensez peut-être "aucun imbécile ne ferait 1000 erreurs d'affilée", mais vous sous-estimez l'ingéniosité des imbéciles!

Kevin
la source
53
C'est amusant de le lire avec de nombreux exemples, bravo. Leçon sous-estimée: "Ne sous-estimez pas l'ingéniosité des imbéciles!"
vpibano
3
Non seulement j'aurais de toute façon surévalué les deux questions et réponses, car elles sont excellentes, mais vous avez scellé l'accord avec "dickety six". Bien joué, @Kevin.
erekalper
1
N'évaluez pas l'ingéniosité des imbéciles ... et des attaquants intelligents. Une attaque DOS serait plus facile pour ce genre de chose, mais d'autres peuvent être possibles.
Solomon Ucko
Peut-on utiliser le nouvel opérateur "morse" au lieu des entrées redondantes? Est-ce aussi un mauvais style?
J Arun Mani
1
@JArunMani Je ne pense pas que ce serait un style médiocre, mais pourrait être un peu moins lisible. Vous n'aurez en effet qu'une seule inputboucle par boucle et la boucle deviendra très courte, mais la condition pourrait devenir assez longue ...
Tomerikoo
39

Pourquoi voudriez-vous faire un while Truepuis sortir de cette boucle alors que vous pouvez également simplement mettre vos exigences dans l'instruction while car tout ce que vous voulez, c'est arrêter une fois que vous avez l'âge?

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

Cela se traduirait par ce qui suit:

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.

cela fonctionnera car l'âge n'aura jamais de valeur qui n'aura pas de sens et le code suit la logique de votre "processus métier"

Steven Stip
la source
22

Bien que la réponse acceptée soit incroyable. Je voudrais également partager un hack rapide pour ce problème. (Cela prend également en charge le problème de l'âge négatif.)

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 Ce code est pour python 3.x.

aaveg
la source
1
Notez que ce code est récursif, mais la récursivité n'est pas nécessaire ici, et comme Kevin l'a dit, cela peut faire exploser votre pile.
PM 2Ring
2
@ PM2Ring - vous avez raison. Mais mon but ici était juste de montrer comment le "court-circuitage" peut minimiser (embellir) de longs morceaux de code.
aaveg
11
Pourquoi assigneriez-vous un lambda à une variable, utilisez-le simplement à la defplace. def f(age):est beaucoup plus clair quef = lambda age:
GP89
3
Dans certains cas, vous pouvez avoir besoin de l'âge une seule fois, puis cette fonction n'est plus utilisée. On peut vouloir utiliser une fonction et la jeter une fois le travail terminé. De plus, ce n'est peut-être pas la meilleure façon, mais c'est certainement une manière différente de le faire (ce qui était le but de ma solution).
aaveg
@aaveg comment transformer ce code pour réellement sauver l'âge fourni par l'utilisateur?
Tytire Recubans
12

Donc, je déconseillais quelque chose de similaire récemment, et j'ai trouvé la solution suivante, qui utilise un moyen d'obtenir une entrée qui rejette les indésirables, avant même qu'elle ne soit vérifiée de manière logique.

read_single_keypress()courtoisie 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()

Vous pouvez trouver le module complet ici .

Exemple:

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

Notez que la nature de cette implémentation est qu'elle ferme stdin dès que quelque chose qui n'est pas un chiffre est lu. Je n'ai pas appuyé sur Entrée après a, mais je devais chercher les chiffres.

Vous pouvez fusionner cela avec la thismany()fonction du même module pour n'autoriser, par exemple, que trois chiffres.

3 tours
la source
12

Approche fonctionnelle ou " regardez maman sans boucles! ":

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

ou si vous voulez avoir un message "mauvaise entrée" séparé d'une invite de saisie comme dans les autres réponses:

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

Comment ça marche?

  1. prompts = chain(["Enter a number: "], repeat("Not a number! Try again: "))
    Cette combinaison de itertools.chainet itertools.repeatcréera un itérateur qui produira des chaînes "Enter a number: "une fois et "Not a number! Try again: "un nombre infini de fois:
    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)- ici map, toutes les promptschaînes de l'étape précédente seront appliquées à la inputfonction. Par exemple:
    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. Nous utilisons filteret str.isdigitpour filtrer les chaînes qui ne contiennent que des chiffres:
    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...
    
    Et pour obtenir uniquement la première chaîne de chiffres uniquement que nous utilisons next.

Autres règles de validation:

  1. Méthodes de chaîne: Bien sûr, vous pouvez utiliser d'autres méthodes de chaîne comme str.isalphapour obtenir uniquement des chaînes alphabétiques ou str.isupperpour obtenir uniquement des majuscules. Voir les documents pour la liste complète.

  2. Test d'adhésion:
    Il existe plusieurs façons de le réaliser. L'un d'eux consiste à utiliser la __contains__méthode:

    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. Comparaison des nombres:
    Il existe des méthodes de comparaison utiles que nous pouvons utiliser ici. Par exemple, pour __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
    

    Ou, si vous n'aimez pas utiliser les méthodes de dunder (dunder = double-underscore), vous pouvez toujours définir votre propre fonction, ou utiliser celles du operatormodule.

  4. Existence de chemin:
    Ici, on peut utiliser la pathlibbibliothèque et sa Path.existsméthode:

    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
    

Nombre d'essais limité:

Si vous ne voulez pas torturer un utilisateur en lui demandant quelque chose un nombre infini de fois, vous pouvez spécifier une limite dans un appel à itertools.repeat. Cela peut être combiné avec la fourniture d'une valeur par défaut à la nextfonction:

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!

Prétraitement des données d'entrée:

Parfois, nous ne voulons pas rejeter une entrée si l'utilisateur l'a accidentellement fournie EN MAJUSCULES ou avec un espace au début ou à la fin de la chaîne. Pour tenir compte de ces erreurs simples, nous pouvons prétraiter les données d'entrée en appliquant les méthodes str.loweret str.strip. Par exemple, dans le cas d'un test d'appartenance, le code ressemblera à ceci:

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

Dans le cas où vous avez de nombreuses fonctions à utiliser pour le prétraitement, il peut être plus facile d'utiliser une fonction exécutant une composition de fonction . Par exemple, en utilisant celui d' ici :

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

Combiner les règles de validation:

Pour un cas simple, par exemple, lorsque le programme demande un âge entre 1 et 120 ans, on peut simplement en ajouter un autre 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)

Mais dans le cas où il existe de nombreuses règles, il est préférable d'implémenter une fonction effectuant une conjonction logique . Dans l'exemple suivant, j'en utiliserai un prêt d' ici :

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

Malheureusement, si quelqu'un a besoin d'un message personnalisé pour chaque cas d'échec, alors, je le crains, il n'y a pas de moyen assez fonctionnel. Ou, du moins, je n'en ai pas trouvé.

Georgy
la source
Quelle réponse complète et merveilleuse, la ventilation des explications était super.
Locane
En utilisant votre style, comment procéder pour supprimer les espaces blancs et réduire les entrées pour les tests d'adhésion? Je ne veux pas créer un ensemble qui doit inclure des exemples en majuscules et en minuscules. Je voudrais également autoriser les erreurs de saisie d'espaces.
Austin
1
@Austin J'ai ajouté une nouvelle section sur le prétraitement. Regarde.
Georgy
Cela me rappelle ReactiveX. Mais peut-être que cela a été inspiré par les langages fonctionnels en premier lieu?
Mateen Ulhaq
8

En utilisant Click :

Click est une bibliothèque d'interfaces de ligne de commande et fournit des fonctionnalités pour demander une réponse valide à un utilisateur.

Exemple simple:

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

Notez comment il a converti automatiquement la valeur de chaîne en un flottant.

Vérification si une valeur se situe dans une plage:

Il existe différents types personnalisés . Pour obtenir un nombre dans une plage spécifique, nous pouvons utiliser 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

Nous pouvons également spécifier une seule des limites, minou 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 d'adhésion:

Utilisation du click.Choicetype. Par défaut, cette vérification respecte la casse.

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

Utilisation des chemins et des fichiers:

En utilisant un click.Pathtype, nous pouvons vérifier les chemins existants et également les résoudre:

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 lecture et l'écriture de fichiers peuvent être effectuées par 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!

Autres exemples:

Confirmation mot de passe:

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

Les valeurs par défaut:

Dans ce cas, une simple pression sur Enter(ou sur la touche que vous utilisez) sans entrer de valeur vous donnera une valeur par défaut:

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
la source
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
la source
2

S'appuyant sur les excellentes suggestions de Daniel Q et Patrick Artner, voici une solution encore plus généralisée.

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

J'ai opté pour explicites ifet des raisedéclarations au lieu d'un assert, parce que la vérification d'assertion peut être désactivée, alors que la validation doit toujours être à fournir robustesse.

Cela peut être utilisé pour obtenir différents types d'entrée, avec différentes conditions de validation. Par exemple:

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

Ou, pour répondre à la question d'origine:

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
la source
1

Essaye celui-là:-

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
la source
0

Bien qu'un bloc try/ exceptfonctionne, une manière beaucoup plus rapide et plus propre d'accomplir cette tâche serait d'utiliser 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.")
2Cubed
la source
0

Bonne question! Vous pouvez essayer le code suivant pour cela. =)

Ce code utilise ast.literal_eval () pour trouver le type de données de l'entrée ( age). Ensuite, il suit l'algorithme suivant:

  1. Demandez à l'utilisateur de saisir son / sa age.

    1.1. Si ageest floatou inttype de données:

    • Vérifiez si age>=18. Si age>=18, imprimez la sortie appropriée et quittez.

    • Vérifiez si 0<age<18. Si 0<age<18, imprimez la sortie appropriée et quittez.

    • Si age<=0, demandez à l'utilisateur de saisir à nouveau un numéro valide pour l'âge ( c. -à-d., Revenez à l'étape 1.)

    1.2. Si ce agen'est pas le cas floatou intle type de données, demandez à l'utilisateur de saisir à nouveau son âge ( c'est -à- dire de revenir à l'étape 1.)

Voici le code.

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
la source
0

Vous pouvez toujours appliquer une logique simple if-else et ajouter une iflogique de plus à votre code avec une forboucle.

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

Ce sera un loo infini et on vous demandera d'entrer l'âge indéfiniment.

Ep1c1aN
la source
Cela ne répond pas vraiment à la question. La question consistait à obtenir une entrée d'utilisateur jusqu'à ce qu'il donne une réponse valide, pas indéfiniment .
Georgy
-1

Vous pouvez écrire une logique plus générale pour permettre à l'utilisateur d'entrer uniquement un nombre spécifique de fois, car le même cas d'utilisation se produit dans de nombreuses applications du monde réel.

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
la source
1
vous oubliez d'augmenter la valeur iCount après chaque boucle
Hoai-Thu Vuong
-1

Vous pouvez faire l'instruction en boucle un certain temps afin qu'elle demande à plusieurs reprises l'entrée des utilisateurs, puis rompre cette boucle si l'utilisateur entre la réponse que vous souhaitez. Et vous pouvez utiliser les blocs try et except pour gérer les réponses invalides.

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 variable var est juste pour que si l'utilisateur entre une chaîne au lieu d'un entier, le programme ne retournera pas "Vous ne pouvez pas voter aux États-Unis".

user9142415
la source
-1

Utilisez l'instruction "while" jusqu'à ce que l'utilisateur saisisse une valeur vraie et si la valeur d'entrée n'est pas un nombre ou s'il s'agit d'une valeur nulle, ignorez-la et essayez de demander à nouveau et ainsi de suite. Par exemple, j'ai essayé de répondre vraiment à votre question. Si nous supposons que notre âge est compris entre 1 et 150, alors la valeur d'entrée est acceptée, sinon c'est une mauvaise valeur. Pour terminer le programme, l'utilisateur peut utiliser la touche 0 et la saisir comme valeur.

Remarque: Lisez les commentaires en haut du code.

# 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
la source
-1

Une autre solution pour utiliser la validation des entrées à l'aide d'une ValidationErrorvalidation de plage personnalisée et (facultative) pour les entrées entières:

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

Usage:

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)

Production:

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
la source
-1

Voici une solution plus propre et plus généralisée qui évite les blocs if / else répétitifs: écrivez une fonction qui prend (erreur, invite d'erreur) dans un dictionnaire et faites toutes vos vérifications de valeur avec des assertions.

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

Usage:

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

Saisie utilisateur persistante utilisant la fonction récursive :

Chaîne

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

name = askName()

Entier

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

age = askAge()

et enfin, l'exigence de la question:

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
la source
-2

La solution simple serait:

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

Explication du code ci-dessus: Pour un âge valide, il doit être positif et ne doit pas être supérieur à l'âge physique normal, par exemple, l'âge maximum est de 120 ans.

Ensuite, nous pouvons demander à l'utilisateur l'âge et si la saisie de l'âge est négative ou supérieure à 120, nous la considérons comme une entrée non valide et demandons à l'utilisateur de réessayer.

Une fois l'entrée valide entrée, nous vérifions (à l'aide d'une instruction imbriquée if-else) si l'âge est> = 18 ou vice versa et imprimons un message si l'utilisateur est habilité à voter

Rohail
la source
"Veuillez entrer votre âge: dickety six" ': même crash que celui indiqué dans la question ...
BDL
-2

prendre l'entrée comme chaîne et utiliser isdigit () pour vérifier que l'entrée n'a que des chiffres, pas vide, ne peut pas être -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

2 tours
la source
Cela ne répond pas non plus à la question.
Georgy