- Вопрос или проблема
- Когда ваш ввод может вызвать исключение
- Реализация собственных правил валидации
- Сочетание обработки исключений и пользовательской валидации
- Инкапсуляция всего в функции
- Собираем все вместе
- Распространенные ошибки и почему их следует избегать
- Избыточное использование избыточных input операторов
- Рекурсия убьет ваш стек
- Функциональный подход или “смотри, мама, никаких циклов!“:
- Как это работает?
- Другие правила валидации:
- Ограничение количества попыток:
- Предварительная обработка входных данных:
- Комбинирование правил валидации:
- Используя Click:
- Проверка, находится ли значение в пределах диапазона:
- Тестирование членства:
- Работа с путями и файлами:
- Другие примеры:
- Подтверждение пароля:
- Значения по умолчанию:
- Строка
- Целое число
- Ответ или решение
- Объяснение кода:
Вопрос или проблема
Я пишу программу, которая принимает ввод пользователя.
#Примечание: пользователи Python 2.7 должны использовать `raw_input`, аналог 3.X `input`
age = int(input("Пожалуйста, введите ваш возраст: "))
if age >= 18:
print("Вы можете голосовать в Соединенных Штатах!")
else:
print("Вы не можете голосовать в Соединенных Штатах.")
Программа работает как ожидается, если пользователь вводит значимые данные.
Пожалуйста, введите ваш возраст: 23
Вы можете голосовать в Соединенных Штатах!
Но она не работает, если пользователь вводит недопустимые данные:
Пожалуйста, введите ваш возраст: dickety six
Traceback (most recent call last):
File "canyouvote.py", line 1, in <module>
age = int(input("Пожалуйста, введите ваш возраст: "))
ValueError: invalid literal for int() with base 10: 'dickety six'
Вместо того, чтобы выдавать ошибку, я хотел бы, чтобы программа снова запрашивала ввод. Вот так:
Пожалуйста, введите ваш возраст: dickety six
Извините, я не понял это.
Пожалуйста, введите ваш возраст: 26
Вы можете голосовать в Соединенных Штатах!
Как мне запросить действительный ввод вместо того, чтобы выдавать ошибку или принимать недопустимые значения (например, -1
)?
Самый простой способ достичь этого – поместить метод input
в цикл while. Используйте continue
при получении плохого ввода и break
, когда вы будете удовлетворены.
Когда ваш ввод может вызвать исключение
Используйте try
и except
, чтобы обнаруживать, когда пользователь вводит данные, которые не могут быть распознаны.
while True:
try:
# Примечание: пользователи Python 2.x должны использовать raw_input, аналог 3.x input
age = int(input("Пожалуйста, введите ваш возраст: "))
except ValueError:
print("Извините, я не понял это.")
# лучше попробовать еще раз... Вернуться в начало цикла
continue
else:
# возраст успешно распознан!
# мы готовы выйти из цикла.
break
if age >= 18:
print("Вы можете голосовать в Соединенных Штатах!")
else:
print("Вы не можете голосовать в Соединенных Штатах.")
Реализация собственных правил валидации
Если вы хотите отклонить значения, которые Python может успешно распознать, вы можете добавить свою собственную логику валидации.
while True:
data = input("Пожалуйста, введите громкое сообщение (должно быть все заглавными буквами): ")
if not data.isupper():
print("Извините, ваш ответ был недостаточно громким.")
continue
else:
# нам нравится данное значение.
# мы готовы выйти из цикла.
break
while True:
data = input("Выберите ответ от A до D:")
if data.lower() not in ('a', 'b', 'c', 'd'):
print("Это не подходящий выбор.")
else:
break
Сочетание обработки исключений и пользовательской валидации
Обе вышеперечисленные техники могут быть объединены в один цикл.
while True:
try:
age = int(input("Пожалуйста, введите ваш возраст: "))
except ValueError:
print("Извините, я не понял это.")
continue
if age < 0:
print("Извините, ваш ответ не должен быть отрицательным.")
continue
else:
# возраст успешно распознан, и нам нравится его значение.
# мы готовы выйти из цикла.
break
if age >= 18:
print("Вы можете голосовать в Соединенных Штатах!")
else:
print("Вы не можете голосовать в Соединенных Штатах.")
Инкапсуляция всего в функции
Если вам нужно запрашивать у пользователя много различных значений, может быть полезно поместить этот код в функцию, чтобы вам не приходилось перепечатывать его каждый раз.
def get_non_negative_int(prompt):
while True:
try:
value = int(input(prompt))
except ValueError:
print("Извините, я не понял это.")
continue
if value < 0:
print("Извините, ваш ответ не должен быть отрицательным.")
continue
else:
break
return value
age = get_non_negative_int("Пожалуйста, введите ваш возраст: ")
kids = get_non_negative_int("Пожалуйста, введите количество детей, которые у вас есть: ")
salary = get_non_negative_int("Пожалуйста, введите ваши годовые доходы в долларах: ")
Собираем все вместе
Вы можете расширить эту идею, чтобы сделать очень универсальную функцию ввода:
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_ должно быть меньше или равно max_.")
while True:
ui = input(prompt)
if type_ is not None:
try:
ui = type_(ui)
except ValueError:
print("Тип ввода должен быть {0}.".format(type_.__name__))
continue
if max_ is not None and ui > max_:
print("Ввод должен быть меньше или равен {0}.".format(max_))
elif min_ is not None and ui < min_:
print("Ввод должен быть больше или равен {0}.".format(min_))
elif range_ is not None and ui not in range_:
if isinstance(range_, range):
template = "Ввод должен быть между {0.start} и {0.stop}."
print(template.format(range_))
else:
template = "Ввод должен быть {0}."
if len(range_) == 1:
print(template.format(*range_))
else:
expected = " или ".join((
", ".join(str(x) for x in range_[:-1]),
str(range_[-1])
))
print(template.format(expected))
else:
return ui
С использованием, например:
age = sanitised_input("Введите ваш возраст: ", int, 1, 101)
answer = sanitised_input("Введите ваш ответ: ", str.lower, range_=('a', 'b', 'c', 'd'))
Распространенные ошибки и почему их следует избегать
Избыточное использование избыточных input
операторов
Этот метод работает, но обычно считается плохим стилем:
data = input("Пожалуйста, введите громкое сообщение (должно быть все заглавными буквами): ")
while not data.isupper():
print("Извините, ваш ответ был недостаточно громким.")
data = input("Пожалуйста, введите громкое сообщение (должно быть все заглавными буквами): ")
Это может казаться привлекательным изначально, потому что короче, чем метод while True
, но он нарушает принцип Не повторяй себя в разработке программного обеспечения. Это увеличивает вероятность ошибок в вашей системе. Что, если вы захотите перейти на 2.7, изменив input
на raw_input
, но случайно измените только первый input
выше? Это SyntaxError
, который только и ждет, чтобы возникнуть.
Рекурсия убьет ваш стек
Если вы только что узнали о рекурсии, вы можете быть склонны использовать ее в get_non_negative_int
, чтобы избавиться от цикла while.
def get_non_negative_int(prompt):
try:
value = int(input(prompt))
except ValueError:
print("Извините, я не понял это.")
return get_non_negative_int(prompt)
if value < 0:
print("Извините, ваш ответ не должен быть отрицательным.")
return get_non_negative_int(prompt)
else:
return value
Это, кажется, работает нормально большую часть времени, но если пользователь введет недопустимые данные достаточно раз, скрипт завершится с RuntimeError: maximum recursion depth exceeded
. Вы можете подумать “ни один дурак не сделает 1000 ошибок подряд”, но вы недооценили изобретательность дураков!
Зачем делать while True
и затем разрываться из этого цикла, если вы можете просто поместить свои требования в оператор while, поскольку все, что вам нужно, – это остановиться, как только у вас есть возраст?
age = None
while age is None:
input_value = input("Пожалуйста, введите ваш возраст: ")
try:
# попытка конвертировать строковый ввод в число
age = int(input_value)
except ValueError:
# сказать пользователю
print("{input} не является числом, пожалуйста, введите только число".format(input=input_value))
if age >= 18:
print("Вы можете голосовать в Соединенных Штатах!")
else:
print("Вы не можете голосовать в Соединенных Штатах.")
Это приведет к следующему:
Пожалуйста, введите ваш возраст: *картошка*
картошка не является числом, пожалуйста, введите только число
Пожалуйста, введите ваш возраст: *5*
Вы не можете голосовать в Соединенных Штатах.
это будет работать, поскольку возраст никогда не будет иметь значения, которые не имеют смысла, и код следует логике вашего “бизнес-процесса”
Функциональный подход или “смотри, мама, никаких циклов!“:
from itertools import chain, repeat
prompts = chain(["Введите число: "], repeat("Это не число! Попробуйте снова: "))
replies = map(input, prompts)
valid_response = next(filter(str.isdigit, replies))
print(valid_response)
Введите число: a
Это не число! Попробуйте снова: b
Это не число! Попробуйте снова: 1
1
или если вы хотите, чтобы сообщение “неверный ввод” было отделено от сообщения с запросом, как в других ответах:
prompt_msg = "Введите число: "
bad_input_msg = "Извините, я не понял это."
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)
Введите число: a
Извините, я не понял это.
Введите число: b
Извините, я не понял это.
Введите число: 1
1
Как это работает?
-
prompts = chain(["Введите число: "], repeat("Это не число! Попробуйте снова: "))
Это сочетание
itertools.chain
иitertools.repeat
создаст итератор
который будет возвращать строки"Введите число: "
один раз, и"Это не число! Попробуйте снова: "
бесконечное число раз:for prompt in prompts: print(prompt)
Введите число: Это не число! Попробуйте снова: Это не число! Попробуйте снова: Это не число! Попробуйте снова: # ... и так далее
replies = map(input, prompts)
– здесьmap
применит все строкиprompts
из предыдущего шага к функцииinput
. Например:for reply in replies: print(reply)
Введите число: a a Это не число! Попробуйте снова: 1 1 Это не число! Попробуйте снова: это не важно это не важно # и так далее...
- Мы используем
filter
иstr.isdigit
, чтобы отфильтровать те строки, которые содержат только цифры:only_digits = filter(str.isdigit, replies) for reply in only_digits: print(reply)
Введите число: a Это не число! Попробуйте снова: 1 1 Это не число! Попробуйте снова: 2 2 Это не число! Попробуйте снова: b Это не число! Попробуйте снова: # и так далее...
И чтобы получить только первую строку, содержащую лишь цифры, мы используем
next
.
Другие правила валидации:
-
Методы строк: Конечно, вы можете использовать другие методы строк, например,
str.isalpha
, чтобы получить только буквы, илиstr.isupper
, чтобы получить только заглавные буквы. Смотрите документацию для полного списка. -
Тестирование членства:
Существует несколько различных способов его выполнения. Один из них – использование__contains__
метода:from itertools import chain, repeat fruits = {'apple', 'orange', 'peach'} prompts = chain(["Введите фрукт: "], repeat("Я не знаю этого! Попробуйте снова: ")) replies = map(input, prompts) valid_response = next(filter(fruits.__contains__, replies)) print(valid_response)
Введите фрукт: 1 Я не знаю этого! Попробуйте снова: foo Я не знаю этого! Попробуйте снова: apple apple
-
Сравнение чисел:
Существуют полезные методы сравнения, которые мы можем использовать здесь. Например, для__lt__
(<
):from itertools import chain, repeat prompts = chain(["Введите положительное число:"], repeat("Мне нужно положительное число! Попробуйте снова:")) 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)
Введите положительное число: a Мне нужно положительное число! Попробуйте снова: -5 Мне нужно положительное число! Попробуйте снова: 0 Мне нужно положительное число! Попробуйте снова: 5 5.0
Или, если вам не нравятся дандер-методы (дуnder = двойное подчеркивание), вы всегда можете определить свою собственную функцию или использовать функции из модуля
operator
. -
Проверка существования пути:
Здесь можно использовать библиотекуpathlib
и ееPath.exists
метод:from itertools import chain, repeat from pathlib import Path prompts = chain(["Введите путь: "], repeat("Этот путь не существует! Попробуйте снова: ")) replies = map(input, prompts) paths = map(Path, replies) valid_response = next(filter(Path.exists, paths)) print(valid_response)
Введите путь: a b c Этот путь не существует! Попробуйте снова: 1 Этот путь не существует! Попробуйте снова: existing_file.txt existing_file.txt
Ограничение количества попыток:
Если вы не хотите мучить пользователя, задавая ему вопрос бесконечное количество раз, вы можете указать лимит в вызове itertools.repeat
. Это можно комбинировать с предоставлением значения по умолчанию для функции next
:
from itertools import chain, repeat
prompts = chain(["Введите число:"], repeat("Это не число! Попробуйте снова:", 2))
replies = map(input, prompts)
valid_response = next(filter(str.isdigit, replies), None)
print("Вы miserably провалились!" if valid_response is None else 'Отлично!')
Введите число: a
Это не число! Попробуйте снова: b
Это не число! Попробуйте снова: c
Вы miserably провалились!
Предварительная обработка входных данных:
Иногда мы не хотим отклонять ввод, если пользователь случайно предоставил его ВСЕМИ ЗАГЛАВНЫМИ или с пробелом в начале или конце строки. Чтобы учитывать эти простые ошибки, мы можем предварительно обработать ввод, применив str.lower
и str.strip
методы. Например, в случае проверки членства код будет выглядеть так:
from itertools import chain, repeat
fruits = {'apple', 'orange', 'peach'}
prompts = chain(["Введите фрукт: "], repeat("Я не знаю этого! Попробуйте снова: "))
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)
Введите фрукт: duck
Я не знаю этого! Попробуйте снова: Orange
orange
В случае, если вам нужно применить много функций для предварительной обработки, может быть проще использовать функцию, выполняющую составление функции. Например, используя которую здесь:
from itertools import chain, repeat
from lz.functional import compose
fruits = {'apple', 'orange', 'peach'}
prompts = chain(["Введите фрукт: "], repeat("Я не знаю этого! Попробуйте снова: "))
replies = map(input, prompts)
process = compose(str.strip, str.lower) # здесь вы можете добавить больше функций
processed_replies = map(process, replies)
valid_response = next(filter(fruits.__contains__, processed_replies))
print(valid_response)
Введите фрукт: potato
Я не знаю этого! Попробуйте снова: PEACH
peach
Комбинирование правил валидации:
Для простого случая, например, когда программа запрашивает возраст от 1 до 120, можно просто добавить еще один filter
:
from itertools import chain, repeat
prompt_msg = "Введите ваш возраст (1-120): "
bad_input_msg = "Неправильный ввод."
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)
Но в случае, если есть много правил, лучше реализовать функцию, выполняющую логическое сложение. В следующем примере я буду использовать готовую из здесь:
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 = "Введите букву (C-P): "
bad_input_msg = "Неправильный ввод."
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)
Введите букву (C-P): 5
Неправильный ввод.
Введите букву (C-P): f
Неправильный ввод.
Введите букву (C-P): CDE
Неправильный ввод.
Введите букву (C-P): Q
Неправильный ввод.
Введите букву (C-P): N
N
К сожалению, если кто-то нуждается в настраиваемом сообщении для каждого случая неудачи, то боюсь, что нет красивого функционального способа. Или, по крайней мере, я не смог найти его.
Хотя принятый ответ потрясающий. Я также хотел бы поделиться быстрым решением этой проблемы. (Это также решает проблему с отрицательным возрастом.)
f=lambda age: (age.isdigit() and ((int(age)>=18 and "Может голосовать" ) or "Не может голосовать")) or \
f(input("недопустимый ввод. Попробуйте еще раз\nПожалуйста, введите ваш возраст: "))
print(f(input("Пожалуйста, введите ваш возраст: ")))
П.С. Этот код написан для python 3.x.
Используя Click:
Click – это библиотека для интерфейсов командной строки, и она предоставляет функциональность для запроса действительного ответа от пользователя.
Простой пример:
import click
number = click.prompt('Пожалуйста, введите число', type=float)
print(number)
Пожалуйста, введите число:
a
Ошибка: a не является действительным числом с плавающей точкой
Пожалуйста, введите число:
10
10.0
Обратите внимание, как он автоматически преобразовал строковое значение в число с плавающей точкой.
Проверка, находится ли значение в пределах диапазона:
Существует разные пользовательские типы. Чтобы получить число в определенном диапазоне, мы можем использовать IntRange
:
age = click.prompt("Сколько вам лет?", type=click.IntRange(1, 120))
print(age)
Сколько вам лет?:
a
Ошибка: a не является действительным целым числом
Сколько вам лет?:
0
Ошибка: 0 не находится в допустимом диапазоне от 1 до 120.
Сколько вам лет?:
5
5
Мы также можем указать только один из пределов, min
или max
:
age = click.prompt("Сколько вам лет?", type=click.IntRange(min=14))
print(age)
Сколько вам лет?:
0
Ошибка: 0 меньше минимально допустимого значения 14.
Сколько вам лет?:
18
18
Тестирование членства:
Используя тип click.Choice
. По умолчанию эта проверка регистронезависимая.
choices = {'apple', 'orange', 'peach'}
choice = click.prompt('Укажите фрукт', type=click.Choice(choices, case_sensitive=False))
print(choice)
Укажите фрукт (apple, peach, orange):
banana
Ошибка: недопустимый выбор: banana. (выберите из apple, peach, orange)
Укажите фрукт (apple, peach, orange):
OrAnGe
orange
Работа с путями и файлами:
Используя тип click.Path
, мы можем проверять существующие пути, а также разрешать их:
path = click.prompt('Укажите путь', type=click.Path(exists=True, resolve_path=True))
print(path)
Укажите путь:
nonexistent
Ошибка: Путь "nonexistent" не существует.
Укажите путь:
existing_folder
'/path/to/existing_folder
Чтение и запись файлов можно осуществить с помощью click.File
:
file = click.prompt('В какой файл записать данные?', type=click.File('w'))
with file.open():
file.write('Hello!')
# Дополнительная информация о `lazy=True` на:
# https://click.palletsprojects.com/en/7.x/arguments/#file-opening-safety
file = click.prompt('Какой файл вы хотите прочитать?', type=click.File(lazy=True))
with file.open():
print(file.read())
В какой файл записать данные?:
# <-- ввел пустую строку, что является недопустимым именем файла
В какой файл записать данные?:
some_file.txt
Какой файл вы хотите прочитать?:
nonexistent.txt
Ошибка: Не удалось открыть файл: nonexistent.txt: Нет такого файла или директории
Какой файл вы хотите прочитать?:
some_file.txt
Hello!
Другие примеры:
Подтверждение пароля:
password = click.prompt('Введите пароль', hide_input=True, confirmation_prompt=True)
print(password)
Введите пароль:
······
Повторите для подтверждения:
·
Ошибка: два введенных значения не совпадают
Введите пароль:
······
Повторите для подтверждения:
······
qwerty
Значения по умолчанию:
В этом случае простое нажатие Enter (или любой другой клавишы, которую вы используете) без ввода значения даст вам значение по умолчанию:
number = click.prompt('Пожалуйста, введите число', type=int, default=42)
print(number)
Пожалуйста, введите число [42]:
a
Ошибка: a не является действительным целым числом
Пожалуйста, введите число [42]:
42
Я большой поклонник философии Unix “Делай одно и делай это хорошо”. Захват ввода пользователя и его валидация – два отдельных этапа:
- запрашивать у пользователя ввод с помощью
get_input
до тех пор, пока ввод не будет корректным - валидация с использованием функции
validator
, которую можно передать вget_input
Это можно сделать так просто (Python 3.8+, с оператором моржа):
def get_input(
prompt="Введите значение: ",
validator=lambda x: True,
error_message="Недопустимый ввод. Пожалуйста, попробуйте снова.",
):
while not validator(value := input(prompt)):
print(error_message)
return value
def is_positive_int(value):
try:
return int(value) >= 0
except ValueError:
return False
if __name__ == "__main__":
val = get_input("Введите положительное число: ", is_positive_int)
print(f"Хорошо, спасибо за {val}")
Пример запуска:
Введите положительное число: -5
Недопустимый ввод. Пожалуйста, попробуйте снова.
Введите положительное число: asdf
Недопустимый ввод. Пожалуйста, попробуйте снова.
Введите положительное число:
Недопустимый ввод. Пожалуйста, попробуйте снова.
Введите положительное число: 42
Хорошо, спасибо за 42
В Python < 3.8 вы могли бы использовать get_input
вот так:
def get_input(
prompt="Введите значение: ",
validator=lambda x: True,
error_message="Недопустимый ввод. Пожалуйста, попробуйте снова.",
):
while True:
value = input(prompt)
if validator(value):
return value
print(error_message)
Вы можете также обработать KeyboardInterrupt
и напечатать дружелюбное сообщение об выходе перед завершением приложения. Счетчик можно использовать для ограничения допустимых попыток, если это необходимо.
Итак, я недавно экспериментировал с чем-то подобным и придумал следующее решение, которое использует способ получения ввода, который отклоняет мусор до того, как он будет проверен логически.
read_single_keypress()
предоставлено https://stackoverflow.com/a/6599441/4532996
def read_single_keypress() -> str:
"""Ожидает одиночный нажатие клавиши на стандартном вводе.
-- из :: https://stackoverflow.com/a/6599441/4532996
"""
import termios, fcntl, sys, os
fd = sys.stdin.fileno()
# сохранить старое состояние
flags_save = fcntl.fcntl(fd, fcntl.F_GETFL)
attrs_save = termios.tcgetattr(fd)
# сделать сырым - способ сделать это взят из man-страницы termios(3).
attrs = list(attrs_save) # копировать сохраненную версию для обновления
# 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)
# отключить неблокирующий режим
fcntl.fcntl(fd, fcntl.F_SETFL, flags_save & ~os.O_NONBLOCK)
# читать одно нажатие клавиши
try:
ret = sys.stdin.read(1) # возвращает один символ
except KeyboardInterrupt:
ret = 0
finally:
# восстановить старое состояние
termios.tcsetattr(fd, termios.TCSAFLUSH, attrs_save)
fcntl.fcntl(fd, fcntl.F_SETFL, flags_save)
return ret
def until_not_multi(chars) -> str:
"""чтение stdin до !(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:
"""практический пример:
проверить, может ли пользователь голосовать, основываясь только на нажатия клавиш"""
print("можете ли вы голосовать? возраст : ", end="")
x = int("0" + until_not_multi("0123456789"))
if not x:
print("\nизвините, возраст может состоять только из цифр.")
return
print("ваш возраст", x, "\nВы можете голосовать!" if x >= 18 else "Извините! Вы не можете голосовать")
_can_you_vote()
Вы можете найти полный модуль здесь.
Пример:
$ ./input_constrain.py
можете ли вы голосовать? возраст : a
извините, возраст может состоять только из цифр.
$ ./input_constrain.py
можете ли вы голосовать? возраст : 23<RETURN>
ваш возраст 23
Вы можете голосовать!
$ _
Обратите внимание, что специфика этой реализации заключается в том, что она закрывает stdin сразу, как только что-то, что не является цифрой, будет прочитано. Я не нажал Enter после a
, но мне нужно было это сделать после цифр.
Вы можете объединить это с thismany()
функцию в том же модуле, чтобы разрешить, скажем, три цифры.
Используйте try-except, чтобы обработать ошибку и повторите ее снова:
while True:
try:
age = int(input("Пожалуйста, введите ваш возраст: "))
if age >= 18:
print("Вы можете голосовать в Соединенных Штатах!")
else:
print("Вы не можете голосовать в Соединенных Штатах.")
except Exception as e:
print("пожалуйста, введите число")
Основываясь на отличных предложениях Даниэля Q и Патрика Артнера,
вот еще одно более общее решение.
# Предполагая Python3
import sys
class ValidationError(ValueError): # спасибо, Патрик Артнер
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: # спасибо, Даниэль Q
print(onerror[type(e)], file=sys.stderr)
Я выбрал явные if
и raise
операторы вместо assert
,
потому что проверка утверждений может быть отключена,
в то время как валидация всегда должна выполняться, чтобы обеспечить надежность.
Это можно использовать для получения различных типов ввода,
с различными условиями валидации.
Например:
# Без валидации, эквивалентно простому вводу:
anystr = validate_input("Введите любую строку: ")
# Получить строку, содержащую только буквы:
letters = validate_input("Введите буквы: ",
cond=str.isalpha,
onerror={ValidationError: "Только буквы, пожалуйста!"})
# Получить число с плавающей точкой в [0, 100]:
percentage = validate_input("Процент? ",
cast=float, cond=lambda x: 0.0<=x<=100.0,
onerror={ValidationError: "Должен быть между 0 и 100!",
ValueError: "Не число!"})
Или, чтобы ответить на исходный вопрос:
age = validate_input("Пожалуйста, введите ваш возраст: ",
cast=int, cond=lambda a:0<=a<150,
onerror={ValidationError: "Введите правдоподобный возраст, пожалуйста!",
ValueError: "Введите целое число, пожалуйста!"})
if age >= 18:
print("Вы можете голосовать в Соединенных Штатах!")
else:
print("Вы не можете голосовать в Соединенных Штатах.")
def validate_age(age):
if age >=0 :
return True
return False
while True:
try:
age = int(raw_input("Пожалуйста, введите ваш возраст:"))
if validate_age(age): break
except ValueError:
print "Ошибка: недопустимый возраст."
Хороший вопрос! Вы можете попробовать следующий код для этого. =)
Этот код использует ast.literal_eval() для определения типа данных ввода (age
). Затем он следует следующему алгоритму:
Попросите пользователя ввести ее/его
age
.1.1. Если
age
имеет типfloat
илиint
:
Проверьте, если
age>=18
. Еслиage>=18
, выведите соответствующий вывод и завершите.Проверьте, если
0<age<18
. Если0<age<18
, выведите соответствующий вывод и завершите.Если
age<=0
, попросите пользователя снова ввести допустимое число для age, (т.е. вернуться к шагу 1.)1.2. Если
age
не имеет типаfloat
илиint
, тогда снова попросите пользователя ввести ее/его возраст (т.е. вернуться к шагу 1.)
Вот код.
from ast import literal_eval
''' Эта функция используется для определения типа данных вводимых данных.'''
def input_type(input_data):
try:
return type(literal_eval(input_data))
except (ValueError, SyntaxError):
return str
flag = True
while(flag):
age = raw_input("Пожалуйста, введите ваш возраст: ")
if input_type(age)==float or input_type(age)==int:
if eval(age)>=18:
print("Вы можете голосовать в Соединенных Штатах!")
flag = False
elif eval(age)>0 and eval(age)<18:
print("Вы не можете голосовать в Соединенных Штатах.")
flag = False
else: print("Пожалуйста, введите допустимое число как ваш возраст.")
else: print("Извините, я не понял это.")
Попробуйте это:-
def takeInput(required):
print 'ooo or OOO to exit'
ans = raw_input('Введите: ')
if not ans:
print "Вы ничего не ввели...!"
return takeInput(required)
## ДЛЯ Выхода ##
elif ans in ['ooo', 'OOO']:
print "Закрытие экземпляра."
exit()
else:
if ans.isdigit():
current="int"
elif set('[~!@#$%^&*()_+{}":/\']+$').intersection(ans):
current="другое"
elif isinstance(ans,basestring):
current="str"
else:
current="none"
if required == current :
return ans
else:
return takeInput(required)
## передайте значение, в котором вы хотите [str/int/специальный символ(как другое )]
print "ввод: ", takeInput('str')
Используйте оператор while
до тех пор, пока пользователь не введет правильное значение, и если вводимое значение не является числом или имеет нулевое значение, пропустите его и попытайтесь снова и так далее.
В примере я попытался достоверно ответить на ваш вопрос. Если предположим, что наш возраст находится в пределах от 1 до 150, тогда вводимые значения принимаются, иначе это неверное значение.
Для завершения программы пользователь может использовать клавишу 0 и ввести ее как значение.
Примечание: Читайте комментарии вверху кода.
# Если ваше вводимое значение является только числом, тогда используйте "Value.isdigit() == False".
# Если вам нужен ввод, который является текстом, вы должны удалить "Value.isdigit() == False".
def Input(Message):
Value = None
while Value == None or Value.isdigit() == False:
try:
Value = str(input(Message)).strip()
except Exception:
Value = None
return Value
# Пример:
age = 0
# Если предположим, что наш возраст находится в пределах от 1 до 150, тогда вводимые значения принимаются,
# иначе это неверное значение.
while age <=0 or age >150:
age = int(Input("Пожалуйста, введите ваш возраст: "))
# Для завершения программы пользователь может использовать клавишу 0 и ввести ее как значение.
if age == 0:
print("Завершение ...")
exit(0)
if age >= 18 and age <=150:
print("Вы можете голосовать в Соединенных Штатах!")
else:
print("Вы не можете голосовать в Соединенных Штатах.")
Вы всегда можете применить простую if-else логику и добавить еще одну if
логику к вашему коду вместе с циклом for
.
while True:
age = int(input("Пожалуйста, введите ваш возраст: "))
if (age >= 18) :
print("Вы можете голосовать в Соединенных Штатах!")
if (age < 18) & (age > 0):
print("Вы не можете голосовать в Соединенных Штатах.")
else:
print("Неверные символы, ввод должен быть числовым")
continue
Это будет бесконечным циклом, и вас будут постоянно просить ввести возраст.
Хотя блок try
/except
сработает, гораздо более быстрым и чистым способом выполнения этой задачи будет использование str.isdigit()
.
while True:
age = input("Пожалуйста, введите ваш возраст: ")
if age.isdigit():
age = int(age)
break
else:
print("Неверное число '{age}'. Попробуйте снова.".format(age=age))
if age >= 18:
print("Вы можете голосовать в Соединенных Штатах!")
else:
print("Вы не можете голосовать в Соединенных Штатах.")
Вы можете написать более общую логику, чтобы разрешить пользователю вводить только конкретное число раз, так как тот же случай использования возникает во многих реальных приложениях.
def getValidInt(iMaxAttemps = None):
iCount = 0
while True:
# выход, когда максимальный лимит попыток истек
if iCount != None and iCount > iMaxAttemps:
return 0 # вернуть как значение по умолчанию
i = raw_input("Введите число")
try:
i = int(i)
except ValueError as e:
print "Введите корректное целочисленное значение"
else:
break
return i
age = getValidInt()
# сделать все, что хотите.
Вы можете сделать оператор ввода циклом while True, чтобы он постоянно запрашивал ввод пользователя, а затем прерывал этот цикл, если пользователь введет нужный ответ. И вы можете использовать блоки try и except для обработки недопустимых ответов.
while True:
var = True
try:
age = int(input("Пожалуйста, введите ваш возраст: "))
except ValueError:
print("Недопустимый ввод.")
var = False
if var == True:
if age >= 18:
print("Вы можете голосовать в Соединенных Штатах.")
break
else:
print("Вы не можете голосовать в Соединенных Штатах.")
Переменная var нужна только для того, чтобы если пользователь вводит строку вместо целого числа, программа не выдавала “Вы не можете голосовать в Соединенных Штатах.”
Еще одно решение для использования валидации ввода с использованием индивидуального ValidationError
и (по желанию) проверки диапазона для целочисленных вводов:
class ValidationError(ValueError):
"""Специальная ошибка валидации - ее сообщение должно выводиться"""
pass
def RangeValidator(text,num,r):
"""Общая валидация - вызывает 'text' как ValidationError, если 'num' не в диапазоне 'r'."""
if num in r:
return num
raise ValidationError(text)
def ValidCol(c):
"""Специальная валидация столбцов, предоставляющая текст и диапазон."""
return RangeValidator("Столбцы должны находиться в диапазоне от 0 до 3 (включительно)",
c, range(4))
def ValidRow(r):
"""Специальная валидация строк, предоставляющая текст и диапазон."""
return RangeValidator("Строки должны находиться в диапазоне от 5 до 15 (исключительно)",
r, range(5,15))
Использование:
def GetInt(text, validator=None):
"""Запрашивает у пользователя ввод целого числа до тех пор, пока не будет введено допустимое целое число. Если предоставлено,
функция 'validator' берет целое число и либо вызывает
ValidationError, чтобы его напечатать, или возвращает допустимое число.
Некорректные целые числа выводят простое сообщение об ошибке."""
print()
while True:
n = input(text)
try:
n = int(n)
return n if validator is None else validator(n)
except ValueError as ve:
# напрямую выводит ValidationErrors - иначе общее сообщение:
if isinstance(ve, ValidationError):
print(ve)
else:
print("Недопустимый ввод: ", n)
column = GetInt("Пожалуйста, введите столбец: ", ValidCol)
row = GetInt("Пожалуйста, введите строку: ", ValidRow)
print( row, column)
Результат:
Пожалуйста, введите столбец: 22
Столбцы должны находиться в диапазоне от 0 до 3 (включительно)
Пожалуйста, введите столбец: -2
Столбцы должны находиться в диапазоне от 0 до 3 (включительно)
Пожалуйста, введите столбец: 2
Пожалуйста, введите строку: a
Недопустимый ввод: a
Пожалуйста, введите строку: 72
Строки должны находиться в диапазоне от 5 до 15 (исключительно)
Пожалуйста, введите строку: 9
9, 2
Постоянный ввод пользователя с использованием рекурсивной функции:
Строка
def askName():
return input("Введите ваше имя: ").strip() or askName()
name = askName()
Целое число
def askAge():
try: return int(input("Введите ваш возраст: "))
except ValueError: return askAge()
age = askAge()
и наконец, требование вопроса:
def askAge():
try: return int(input("Введите ваш возраст: "))
except ValueError: return askAge()
age = askAge()
responseAge = [
"Вы можете голосовать в Соединенных Штатах!",
"Вы не можете голосовать в Соединенных Штатах.",
][int(age < 18)]
print(responseAge)
Вы можете попытаться преобразовать это в целое число, но попросите пользователя повторить, если это не сработает.
while True:
age = input('Пожалуйста, введите ваш возраст: ')
try:
age_int = int(age)
if age_int >= 18:
print('Вы можете голосовать в Соединенных Штатах!')
else:
print('Вы не можете голосовать в Соединенных Штатах.')
break
except:
print('Пожалуйста, введите осмысленный ответ.')
Цикл while работает до тех пор, пока пользователь не введет осмысленный ответ, но завершается, если он имеет смысл.
Используйте isdigit()
, чтобы проверить, представляет ли строка действительное целое число.
Вы можете использовать рекурсивную функцию.
def ask():
answer = input("Пожалуйста, введите сумму для конвертации: ")
if not answer.isdigit():
print("Недопустимый ввод")
return ask()
return int(answer)
Gdp = ask()
Или цикл while
while True:
answer = input("Пожалуйста, введите сумму для конвертации: ")
if not answer.isdigit():
print("Недопустимый ввод")
continue
Gbp = int(answer)
Ниже приведенный код может помочь.
age=(lambda i,f: f(i,f))(input("Пожалуйста, введите ваш возраст: "),lambda i,f: i if i.isdigit() else f(input("Пожалуйста, введите ваш возраст: "),f))
print("Вы можете голосовать в соединенных штатах" if int(age)>=18 else "Вы не можете голосовать в соединенных штатах",end='')
Если хотите ограничить количество попыток, скажем 3, используйте следующий код
age=(lambda i,n,f: f(i,n,f))(input("Пожалуйста, введите ваш возраст: "),1,lambda i,n,f: i if i.isdigit() else (None if n==3 else f(input("Пожалуйста, введите ваш возраст: "),n+1,f)))
print("Вы можете голосовать в соединенных штатах" если age и int(age)>=18 else "Вы не можете голосовать в соединенных штатах",end='')
Примечание: Это использует рекурсию.
Ответ или решение
Чтобы создать программу, которая запрашивает возраст пользователя и продолжает запрашивать ввод до тех пор, пока этот ввод не будет действительным, вы можете использовать цикл while
в сочетании с обработкой исключений. Приведённый ниже код демонстрирует эту логику:
while True:
try:
age = int(input("Пожалуйста, введите ваш возраст: "))
# Проверка на минимальный и максимальный возраст
if age < 0:
print("Возраст не может быть отрицательным. Пожалуйста, попробуйте снова.")
continue
# Взрослый ли пользователь
if age >= 18:
print("Вы имеете право голосовать в Соединенных Штатах!")
else:
print("Вы не имеете права голосовать в Соединенных Штатах.")
break # Выход из цикла, если ввод валиден
except ValueError:
print("Извините, я не понял это. Пожалуйста, введите число.")
Объяснение кода:
-
Цикл
while True
: Этот цикл будет повторяться до тех пор, пока программой не будет выполнено явное прерывание (например, с помощьюbreak
). -
Обработка исключений: Используя конструкцию
try-except
, программа пытается преобразовать ввод пользователя в целое число. Если ввод некорректен (например, если пользователь вводит буквы), программа срабатывает на исключениеValueError
и выводит сообщение об ошибке. -
Проверка возраста: После успешного преобразования возраста программа проверяет, является ли возраст отрицательным. Если да, выводится сообщение, и пользователь может попробовать снова.
-
Определение права на голосование: Если возраст корректный и равен или больше 18, пользователю сообщается о праве голоса. В противном случае — о его отсутствии.
- Выход из цикла: После корректного ввода и обработки программы происходит выход из цикла
break
.
Этот код является простым и эффективным способом ввести данные от пользователя с необходимыми проверками, что предотвращает возможные ошибки и недоразумения.