Solicitando entrada ao usuário até que ele dê uma resposta válida

562

Estou escrevendo um programa que aceita uma entrada do usuário.

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

O programa funciona conforme o esperado, desde que o usuário insira dados significativos.

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

Mas falha se o usuário digitar dados inválidos:

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'

Em vez de travar, eu gostaria que o programa pedisse a entrada novamente. Como isso:

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!

Como posso fazer com que o programa solicite entradas válidas em vez de travar quando dados não sensoriais são inseridos?

Como posso rejeitar valores como -1, que é válido int, mas sem sentido neste contexto?

Kevin
fonte

Respostas:

704

A maneira mais simples de fazer isso é colocar o inputmétodo em um loop while. Use continuequando receber uma entrada ruim e breaksair do circuito quando estiver satisfeito.

Quando sua entrada pode gerar uma exceção

Use tryeexcept para detectar quando o usuário digita dados que não podem ser analisados.

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

Implementando suas próprias regras de validação

Se você deseja rejeitar valores que o Python pode analisar com êxito, você pode adicionar sua própria lógica de validação.

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

Combinando tratamento de exceções e validação personalizada

Ambas as técnicas acima podem ser combinadas em um 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.")

Encapsulando tudo em uma função

Se você precisar solicitar muitos valores diferentes ao usuário, pode ser útil colocar esse código em uma função, para que você não precise digitá-lo novamente todas as vezes.

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

Juntando tudo

Você pode estender essa ideia para criar uma função de entrada muito genérica:

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

Com uso como:

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

Armadilhas comuns e por que você deve evitá-las

O uso redundante de inputdeclarações redundantes

Esse método funciona, mas geralmente é considerado de estilo pobre:

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

Pode parecer atraente inicialmente porque é mais curto que o while Truemétodo, mas viola o princípio de não se repetir de desenvolvimento de software. Isso aumenta a probabilidade de erros no seu sistema. E se você quiser voltar para o 2.7 alterando inputpara raw_input, mas alterar acidentalmente apenas o primeiro inputacima? Está SyntaxErrorapenas esperando para acontecer.

A recursão explodirá sua pilha

Se você acabou de aprender sobre recursão, pode ser tentado a usá-lo get_non_negative_intpara poder descartar o loop 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

Isso parece funcionar bem na maioria das vezes, mas se o usuário digitar dados inválidos várias vezes, o script será encerrado com a RuntimeError: maximum recursion depth exceeded. Você pode pensar que "nenhum tolo cometeria 1000 erros seguidos", mas está subestimando a ingenuidade dos tolos!

Kevin
fonte
53
É divertido lê-lo com muitos exemplos, parabéns. Lição subestimada: "Não subestime a ingenuidade dos tolos!"
vpibano
3
Não apenas eu teria votado nas perguntas e respostas de qualquer maneira, como elas são ótimas, mas você selou o acordo com "idiota e seis". Muito bem, @ Kevin.
erekalper
1
Não avalie a ingenuidade de tolos ... e atacantes inteligentes. Um ataque do DOS seria mais fácil para esse tipo de coisa, mas outras podem ser possíveis.
Solomon Ucko
Podemos usar o novo operador "morsa" em vez de entradas redundantes? Também é um estilo ruim?
J Arun Mani
1
@JArunMani Eu não acho que seria um estilo ruim, mas pode ser um pouco menos legível. Você realmente terá apenas um inputpor loop e o loop ficará muito curto, mas a condição pode se tornar bastante longa ...
Tomerikoo
39

Por que você faria um while Truee, em seguida, interromperá esse ciclo enquanto também pode colocar seus requisitos na instrução while, pois tudo o que você deseja é parar quando tiver a idade?

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

Isso resultaria no seguinte:

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.

isso funcionará, pois a idade nunca terá um valor que não fará sentido e o código seguirá a lógica do seu "processo de negócios"

Steven Stip
fonte
22

Embora a resposta aceita seja incrível. Eu também gostaria de compartilhar um hack rápido para esse problema. (Isso também resolve o problema da idade 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 Este código é para python 3.x.

aaveg
fonte
1
Observe que esse código é recursivo, mas a recursão não é necessária aqui e, como Kevin disse, pode explodir sua pilha.
usar o seguinte comando
2
@ PM2Ring - você está certo. Mas meu objetivo aqui era apenas mostrar como o "curto-circuito" pode minimizar (embelezar) longos pedaços de código.
aaveg
11
Por que você atribuiria um lambda a uma variável, basta usar def. def f(age):é muito mais claro do quef = lambda age:
GP89 16/05
3
Em alguns casos, você pode precisar da idade apenas uma vez e, em seguida, não há uso dessa função. Pode-se usar uma função e jogá-la fora após o término do trabalho. Além disso, essa pode não ser a melhor maneira, mas definitivamente é uma maneira diferente de fazer isso (que era o objetivo da minha solução).
aaveg
@aaveg, como você ativaria esse código para realmente salvar a idade fornecida pelo usuário?
Tytire Recubans
12

Então, eu estava mexendo com algo parecido com isso recentemente, e vim com a seguinte solução, que usa uma maneira de obter entradas que rejeitam lixo, antes mesmo de serem verificadas de maneira lógica.

read_single_keypress()cortesia de 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()

Você pode encontrar o módulo completo aqui .

Exemplo:

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

Observe que a natureza dessa implementação é que ela fecha o stdin assim que algo que não é um dígito é lido. Não apertei enter depois a, mas precisava depois dos números.

Você pode mesclar isso com a thismany()função no mesmo módulo para permitir apenas, digamos, três dígitos.

3 rotações
fonte
12

Abordagem funcional ou " olhe mum no loops! ":

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 se você deseja que uma mensagem de "entrada incorreta" seja separada de um prompt de entrada, como em outras respostas:

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

Como funciona?

  1. prompts = chain(["Enter a number: "], repeat("Not a number! Try again: "))
    Essa combinação de itertools.chaine itertools.repeatcriará um iterador que produzirá seqüências de caracteres "Enter a number: "uma vez e "Not a number! Try again: "um número infinito de vezes:
    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)- aqui mapaplicará todas as promptsstrings da etapa anterior à inputfunção. Por exemplo:
    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. Usamos filtere str.isdigitpara filtrar as strings que contêm apenas dígitos:
    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 para obter apenas a primeira string que usamos apenas dígitos next.

Outras regras de validação:

  1. Métodos de seqüência de caracteres: é claro que você pode usar outros métodos de seqüência de caracteres, como str.isalphaobter apenas seqüências alfabéticas ou str.isupperobter apenas maiúsculas. Veja os documentos para a lista completa.

  2. Teste de associação:
    Existem várias maneiras diferentes de realizá-lo. Um deles é usando o __contains__método:

    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. Comparação de números:
    Existem métodos úteis de comparação que podemos usar aqui. Por exemplo, para __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, se você não gosta de usar métodos dunder (dunder = sublinhado duplo), sempre pode definir sua própria função ou usar as do operatormódulo.

  4. Existência de caminho:
    Aqui é possível usar a pathlibbiblioteca e seu Path.existsmétodo:

    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

Limitar o número de tentativas:

Se você não quiser torturar um usuário, perguntando-lhe algo inúmeras vezes, poderá especificar um limite em uma chamada de itertools.repeat. Isso pode ser combinado com o fornecimento de um valor padrão para a nextfunção:

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é-processamento de dados de entrada:

Às vezes, não queremos rejeitar uma entrada se o usuário a fornecer acidentalmente no CA CAPS ou com um espaço no início ou no final da string. Para tirar esses erros simples em conta que pode pré-processar os dados de entrada através da aplicação str.lowere str.stripmétodos. Por exemplo, para o caso de teste de associação, o código terá a seguinte aparência:

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

No caso em que você possui muitas funções para pré-processamento, pode ser mais fácil usar uma função executando uma composição de função . Por exemplo, usando o daqui :

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

Combinando regras de validação:

Para um caso simples, por exemplo, quando o programa solicita idade entre 1 e 120, é possível adicionar outro 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)

Porém, no caso de existirem muitas regras, é melhor implementar uma função executando uma conjunção lógica . No exemplo a seguir, usarei um pronto aqui :

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

Infelizmente, se alguém precisar de uma mensagem personalizada para cada caso com falha, receio que não exista uma maneira funcional. Ou, pelo menos, não consegui encontrar um.

Georgy
fonte
Que resposta completa e maravilhosa, a explicação foi ótima.
Locane
Usando o seu estilo, como remover um espaço em branco e aplicar uma classificação inferior à entrada para teste de associação? Não quero criar um conjunto que deve incluir exemplos em maiúsculas e minúsculas. Eu também gostaria de permitir erros de entrada de espaço em branco.
Austin
1
@ Austin Adicionei uma nova seção sobre pré-processamento. Dê uma olhada.
Georgy
Isso me lembra o ReactiveX. Mas talvez isso tenha sido inspirado por linguagens funcionais em primeiro lugar?
Mateen Ulhaq 06/06
8

Usando o Click :

Click é uma biblioteca para interfaces de linha de comando e fornece funcionalidade para solicitar uma resposta válida de um usuário.

Exemplo simples:

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

Observe como ele converteu o valor da string em um flutuador automaticamente.

Verificando se um valor está dentro de um intervalo:

Existem diferentes tipos personalizados fornecidos. Para obter um número em um intervalo específico, podemos usar 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

Também podemos especificar apenas um dos 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

Teste de associação:

Usando o click.Choicetipo Por padrão, essa verificação diferencia maiúsculas de minúsculas.

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

Trabalhando com caminhos e arquivos:

Usando um click.Pathtipo, podemos verificar os caminhos existentes e também resolvê-los:

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

A leitura e gravação de arquivos podem ser feitas por 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!

Outros exemplos:

ConfirmaÇão Da Senha:

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

Valores padrão:

Nesse caso, simplesmente pressionar Enter(ou qualquer tecla que você usar) sem inserir um valor, fornecerá um valor padrão:

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
fonte
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
fonte
2

Com base nas excelentes sugestões de Daniel Q e Patrick Artner, aqui está uma solução ainda mais generalizada.

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

Optei por instruções explícitas ife raisenão por uma assert, porque a verificação de asserções pode estar desativada, enquanto a validação deve estar sempre ativada para fornecer robustez.

Isso pode ser usado para obter diferentes tipos de entrada, com diferentes condições de validação. Por exemplo:

# 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, para responder à pergunta original:

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
fonte
1

Tente este:-

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
fonte
0

Enquanto um bloco try/ exceptfuncionará, uma maneira muito mais rápida e limpa de realizar essa tarefa seria usar 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
fonte
0

Boa pergunta! Você pode tentar o seguinte código para isso. =)

Este código usa ast.literal_eval () para encontrar o tipo de dados da entrada ( age). Em seguida, segue o seguinte algoritmo:

  1. Peça ao usuário para inseri-lo age.

    1.1 Se ageé floatou inttipo de dados:

    • Verifique se age>=18. Se age>=18, imprima a saída apropriada e saia.

    • Verifique se 0<age<18. Se 0<age<18, imprima a saída apropriada e saia.

    • Se age<=0, solicitar que o usuário insira um número válido para a idade novamente, ( ou seja, voltar para a etapa 1.)

    1.2 Se agenão for floatou inttipo de dados, peça ao usuário para inserir sua idade novamente ( ou seja, volte para a etapa 1.)

Aqui está o código.

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
fonte
0

Você sempre pode aplicar uma lógica if-else simples e adicionar mais uma iflógica ao seu código, juntamente com um forloop.

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

Este será um banheiro infinito e você será solicitado a inserir a idade indefinidamente.

Ep1c1aN
fonte
Isso realmente não responde à pergunta. A pergunta era sobre obter uma entrada do usuário até que eles dessem uma resposta válida, não indefinidamente .
Georgy
-1

Você pode escrever uma lógica mais geral para permitir que o usuário insira apenas um número específico de vezes, pois o mesmo caso de uso surge em muitos aplicativos do mundo real.

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
fonte
1
você se esqueça de aumentar o valor iCount após cada ciclo
Hoai Thu-Vuong
-1

Você pode fazer a declaração de entrada um loop True while, para que ele solicite repetidamente a entrada do usuário e, em seguida, interrompa o loop se o usuário digitar a resposta que você deseja. E você pode usar try e except blocos para lidar com respostas inválidas.

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

A variável var é apenas para que, se o usuário digitar uma string em vez de um número inteiro, o programa não retorne "Você não pode votar nos Estados Unidos".

user9142415
fonte
-1

Use a instrução "while" até que o usuário insira um valor verdadeiro e, se o valor de entrada não for um número ou for um valor nulo, pule-o e tente perguntar novamente e assim por diante. No exemplo, tentei responder verdadeiramente à sua pergunta. Se supusermos que nossa idade está entre 1 e 150, então o valor de entrada é aceito, caso contrário, é um valor errado. Para encerrar o programa, o usuário pode usar a tecla 0 e inseri-la como um valor.

Nota: Leia os comentários na parte superior do código.

# 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
fonte
-1

Mais uma solução para usar a validação de entrada usando uma validação personalizada ValidationErrore uma faixa (opcional) para entradas inteiras:

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)

Resultado:

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
fonte
-1

Aqui está uma solução mais limpa e generalizada que evita blocos repetitivos if / else: escreva uma função que aceita pares (Erro, prompt de erro) em um dicionário e faça toda a sua verificação de valor com asserções.

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)
Daniel Q
fonte
-1

Entrada persistente do usuário usando a função recursiva :

Corda

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

name = askName()

Inteiro

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

age = askAge()

e, finalmente, o requisito de pergunta:

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
fonte
-2

A solução simples seria:

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

Explicação do código acima: Para uma idade válida, ela deve ser positiva e não deve ser maior que a idade física normal, por exemplo, a idade máxima é 120.

Em seguida, podemos solicitar a idade do usuário e, se a entrada por idade for negativa ou superior a 120, consideraremos a entrada inválida e solicitaremos que o usuário tente novamente.

Depois que a entrada válida é inserida, realizamos uma verificação (usando a instrução if-else aninhada) se a idade é> = 18 ou vice-versa e imprimimos uma mensagem se o usuário está qualificado para votar

Rohail
fonte
"Por favor, digite sua idade: idiota e seis" ': o mesmo acidente conforme indicado na pergunta ...
BDL
-2

pegue a entrada como string e use isdigit () para verificar se a entrada possui apenas dígitos, não está vazia, não pode ser -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

Dead Pool
fonte
Também não responde à pergunta.
Georgy