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?
prompts = chain(["Enter a number: "], repeat("Not a number! Try again: "))
Cette combinaison de itertools.chain
et itertools.repeat
cré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
replies = map(input, prompts)
- ici map
, toutes les prompts
chaînes de l'étape précédente seront appliquées à la input
fonction. 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...
- Nous utilisons
filter
et str.isdigit
pour 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:
Méthodes de chaîne: Bien sûr, vous pouvez utiliser d'autres méthodes de chaîne comme str.isalpha
pour obtenir uniquement des chaînes alphabétiques ou str.isupper
pour obtenir uniquement des majuscules. Voir les documents pour la liste complète.
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
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 operator
module.
Existence de chemin:
Ici, on peut utiliser la pathlib
bibliothèque et sa Path.exists
mé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 next
fonction:
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.lower
et 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é.