Meminta input dari pengguna sampai mereka memberikan respons yang valid

562

Saya menulis sebuah program yang menerima input dari pengguna.

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

Program ini berfungsi seperti yang diharapkan selama pengguna memasukkan data yang bermakna.

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

Tetapi gagal jika pengguna memasukkan data yang tidak valid:

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'

Alih-alih menabrak, saya ingin program meminta input lagi. Seperti ini:

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!

Bagaimana saya bisa membuat program meminta input yang valid alih-alih mogok ketika data yang tidak masuk akal dimasukkan?

Bagaimana saya bisa menolak nilai seperti -1, yang valid int, tetapi tidak masuk akal dalam konteks ini?

Kevin
sumber

Jawaban:

704

Cara paling sederhana untuk mencapai ini adalah dengan meletakkan inputmetode dalam loop sementara. Gunakan continuesaat Anda mendapat input yang buruk, dan breakkeluar dari lingkaran saat Anda puas.

Saat Masukan Anda Mungkin Mendapat Pengecualian

Gunakan trydanexcept untuk mendeteksi ketika pengguna memasukkan data yang tidak dapat diuraikan.

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

Menerapkan Aturan Validasi Anda Sendiri

Jika Anda ingin menolak nilai yang Python berhasil parse, Anda bisa menambahkan logika validasi Anda sendiri.

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

Menggabungkan Penanganan Eksepsi dan Validasi Kustom

Kedua teknik di atas dapat digabungkan menjadi satu 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.")

Enkapsulasi Semuanya dalam suatu Fungsi

Jika Anda perlu meminta banyak nilai berbeda kepada pengguna, mungkin berguna untuk menempatkan kode ini dalam suatu fungsi, jadi Anda tidak perlu mengetik ulang setiap kali.

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

Menyatukan Semuanya

Anda dapat memperluas ide ini untuk membuat fungsi input yang sangat umum:

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

Dengan penggunaan seperti:

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

Perangkap Umum, dan Mengapa Anda Harus Menghindarinya

Penggunaan Redundant inputStatement Redundant

Metode ini berfungsi tetapi umumnya dianggap sebagai gaya yang buruk:

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

Awalnya mungkin terlihat menarik karena lebih pendek dari while Truemetodenya, tetapi melanggar prinsip Jangan Ulangi Pengembangan Perangkat Lunak. Ini meningkatkan kemungkinan bug di sistem Anda. Bagaimana jika Anda ingin backport ke 2.7 dengan mengubah inputke raw_input, tetapi secara tidak sengaja hanya mengubah yang pertama di inputatas? Itu adalahSyntaxError hanya menunggu untuk terjadi.

Rekursi Akan Memukul Tumpukan Anda

Jika Anda baru saja belajar tentang rekursi, Anda mungkin tergoda untuk menggunakannya get_non_negative_intsehingga Anda dapat membuang loop sementara.

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

Ini tampaknya berfungsi dengan baik sebagian besar waktu, tetapi jika pengguna memasukkan data yang tidak valid cukup kali, skrip akan diakhiri dengan a RuntimeError: maximum recursion depth exceeded. Anda mungkin berpikir "tidak ada orang bodoh yang akan membuat 1000 kesalahan berturut-turut", tetapi Anda meremehkan kecerdikan orang bodoh!

Kevin
sumber
53
Sangat menyenangkan membacanya dengan banyak contoh, pujian. Pelajaran yang diremehkan: "Jangan meremehkan kecerdikan orang bodoh!"
vpibano
3
Saya tidak hanya akan meningkatkan Q&A, karena mereka hebat, tetapi Anda juga menyetujui kesepakatan dengan "dickety six". Bagus sekali, @Kevin.
erekalper
1
Jangan memperkirakan kecerdikan orang bodoh ... dan penyerang yang pintar. Serangan DOS akan menjadi yang termudah untuk hal semacam ini, tetapi yang lain mungkin dilakukan.
Solomon Ucko
Bisakah kita menggunakan operator "walrus" yang baru dan bukannya input yang berlebihan? Apakah itu juga gaya yang buruk?
J Arun Mani
1
@JArunMani Saya tidak berpikir itu akan menjadi gaya yang buruk, tetapi mungkin sedikit kurang mudah dibaca. Anda memang hanya memiliki satu inputper loop dan loop akan menjadi sangat pendek, tetapi kondisinya mungkin menjadi cukup lama ...
Tomerikoo
39

Mengapa Anda melakukan while Truedan kemudian keluar dari lingkaran ini sementara Anda juga bisa memasukkan persyaratan Anda dalam pernyataan sementara karena semua yang Anda inginkan adalah berhenti setelah Anda memiliki usia?

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

Ini akan menghasilkan sebagai berikut:

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.

ini akan berfungsi karena usia tidak akan pernah memiliki nilai yang tidak masuk akal dan kode mengikuti logika "proses bisnis" Anda

Steven Stip
sumber
22

Padahal jawaban yang diterima luar biasa. Saya juga ingin membagikan peretasan cepat untuk masalah ini. (Ini menangani masalah usia negatif juga.)

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 Kode ini untuk python 3.x.

aaveg
sumber
1
Perhatikan bahwa kode ini bersifat rekursif, tetapi rekursi tidak diperlukan di sini, dan seperti yang dikatakan Kevin, kode ini dapat meledakkan tumpukan Anda.
PM 2Ring
2
@ PM2Ring - Anda benar. Tetapi tujuan saya di sini adalah hanya untuk menunjukkan bagaimana "hubungan arus pendek" dapat meminimalkan (memperindah) potongan kode yang panjang.
aaveg
11
Mengapa Anda menetapkan lambda ke variabel, cukup gunakan defsaja. def f(age):jauh lebih jelas daripadaf = lambda age:
GP89
3
Dalam beberapa kasus, Anda mungkin perlu usia hanya sekali dan kemudian tidak ada gunanya fungsi itu. Seseorang mungkin ingin menggunakan fungsi dan membuangnya setelah pekerjaan selesai. Juga, ini mungkin bukan cara terbaik, tetapi jelas cara yang berbeda untuk melakukannya (yang merupakan tujuan dari solusi saya).
aaveg
@ aaveg bagaimana Anda mengubah kode ini untuk benar-benar menghemat usia yang disediakan oleh pengguna?
Tytire Recubans
12

Jadi, saya bermain-main dengan sesuatu yang mirip dengan ini baru-baru ini, dan saya datang dengan solusi berikut, yang menggunakan cara mendapatkan input yang menolak sampah, bahkan sebelum diperiksa dengan cara yang logis.

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

Anda dapat menemukan modul lengkap di sini .

Contoh:

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

Perhatikan bahwa sifat implementasi ini adalah menutup stdin segera setelah sesuatu yang bukan angka dibaca. Saya tidak menekan enter setelah a, tetapi saya perlu setelah angka.

Anda bisa menggabungkan ini dengan thismany()fungsi dalam modul yang sama hanya memungkinkan, katakanlah, tiga digit.

cat
sumber
12

Pendekatan fungsional atau " lihat ibu tidak ada loop! ":

from itertools import chain, repeat

prompts = chain(["Enter a number: "], repeat("Not a number! Try again: "))
replies = map(input, prompts)
valid_response = next(filter(str.isdigit, replies))
print(valid_response)
Enter a number:  a
Not a number! Try again:  b
Not a number! Try again:  1
1

atau jika Anda ingin memisahkan pesan "input buruk" dari prompt input seperti pada jawaban lain:

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

Bagaimana cara kerjanya?

  1. prompts = chain(["Enter a number: "], repeat("Not a number! Try again: "))
    Kombinasi dari itertools.chaindan itertools.repeatakan menciptakan iterator yang akan menghasilkan string "Enter a number: "sekali, dan "Not a number! Try again: "dalam jumlah tak terbatas:
    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)- di sini mapakan menerapkan semua promptsstring dari langkah sebelumnya ke inputfungsi. Misalnya:
    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. Kami menggunakan filterdan str.isdigitmemfilter string yang hanya berisi digit:
    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...
    Dan untuk mendapatkan hanya string digit-only pertama yang kami gunakan next.

Aturan validasi lainnya:

  1. Metode string: Tentu saja Anda dapat menggunakan metode string lain seperti str.isalphahanya mendapatkan string alfabet, atau str.isupperhanya mendapatkan huruf besar. Lihat dokumen untuk daftar lengkap.

  2. Pengujian keanggotaan:
    Ada beberapa cara berbeda untuk melakukannya. Salah satunya dengan menggunakan __contains__metode:

    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. Perbandingan angka:
    Ada metode perbandingan berguna yang bisa kita gunakan di sini. Misalnya, untuk __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

    Atau, jika Anda tidak suka menggunakan metode dunder (dunder = double-underscore), Anda selalu dapat mendefinisikan fungsi Anda sendiri, atau menggunakan yang dari operatormodul.

  4. Jalur keberadaan:
    Di sini orang dapat menggunakan pathlibperpustakaan dan Path.existsmetodenya:

    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

Membatasi jumlah percobaan:

Jika Anda tidak ingin menyiksa pengguna dengan menanyakan sesuatu sesuatu dalam jumlah tak terbatas, Anda dapat menentukan batas dalam panggilan itertools.repeat. Ini dapat dikombinasikan dengan memberikan nilai default ke nextfungsi:

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!

Input data preprocessing:

Terkadang kami tidak ingin menolak input jika pengguna secara tidak sengaja memasukkannya dalam CAPS atau dengan spasi di awal atau akhir dari string. Untuk memperhitungkan kesalahan sederhana ini, kita dapat memproses ulang input data dengan menerapkan str.lowerdan str.stripmetode. Misalnya, untuk kasus pengujian keanggotaan, kode akan terlihat seperti ini:

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

Dalam kasus ketika Anda memiliki banyak fungsi untuk digunakan untuk preprocessing, mungkin lebih mudah untuk menggunakan fungsi yang melakukan komposisi fungsi . Misalnya, menggunakan yang dari sini :

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

Menggabungkan aturan validasi:

Untuk kasus sederhana, misalnya, ketika program meminta usia antara 1 dan 120, orang bisa menambahkan yang lain 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)

Tetapi dalam kasus ketika ada banyak aturan, lebih baik untuk mengimplementasikan fungsi yang melakukan konjungsi logis . Dalam contoh berikut ini saya akan menggunakan yang siap dari sini :

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

Sayangnya, jika seseorang membutuhkan pesan khusus untuk setiap kasus yang gagal, maka, saya khawatir, tidak ada yang cantik cara yang fungsional. Atau, setidaknya, saya tidak dapat menemukannya.

Georgy
sumber
Sungguh jawaban yang teliti dan luar biasa, uraian penjelasannya hebat.
Locane
Dengan menggunakan gaya Anda, bagaimana cara menanggalkan ruang kosong dan mengurangi masukan untuk pengujian keanggotaan? Saya tidak ingin membuat himpunan yang harus menyertakan contoh huruf besar dan kecil. Saya juga ingin mengizinkan kesalahan input spasi putih.
Austin
1
@Austin saya menambahkan bagian baru pada preprocessing. Lihatlah.
Georgy
Itu mengingatkan saya pada ReactiveX. Tapi mungkin itu terinspirasi oleh bahasa fungsional?
Mateen Ulhaq
8

Menggunakan Klik :

Klik adalah pustaka untuk antarmuka baris perintah dan menyediakan fungsionalitas untuk meminta respons yang valid dari pengguna.

Contoh sederhana:

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

Perhatikan bagaimana ini mengubah nilai string menjadi float secara otomatis.

Memeriksa apakah suatu nilai berada dalam kisaran:

Ada berbagai jenis kustom yang disediakan. Untuk mendapatkan nomor dalam rentang tertentu, kita dapat menggunakan 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

Kami juga dapat menentukan satu saja dari batasan, minatau 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

Pengujian keanggotaan:

Menggunakan click.Choicetipe. Secara default, pemeriksaan ini peka terhadap huruf besar-kecil.

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

Bekerja dengan jalur dan file:

Menggunakan click.Pathtipe kita dapat memeriksa jalur yang ada dan juga menyelesaikannya:

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

Membaca dan menulis file dapat dilakukan dengan 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!

Contoh lain:

Konfirmasi kata kunci:

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

Nilai dasar:

Dalam hal ini, cukup menekan Enter(atau tombol apa pun yang Anda gunakan) tanpa memasukkan nilai, akan memberi Anda nilai default:

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

Berdasarkan saran Daniel Q dan Patrick Artner yang luar biasa, inilah solusi yang lebih umum.

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

Saya memilih pernyataan yang eksplisit ifdan raisebukan assert, karena pemeriksaan pernyataan dapat dimatikan, sedangkan validasi harus selalu aktif untuk memberikan ketahanan.

Ini dapat digunakan untuk mendapatkan berbagai jenis input, dengan kondisi validasi yang berbeda. Sebagai contoh:

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

Atau, untuk menjawab pertanyaan awal:

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

Coba yang ini:-

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

Sementara a try/ exceptblock akan bekerja, cara yang jauh lebih cepat dan lebih bersih untuk menyelesaikan tugas ini adalah dengan menggunakannya 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
sumber
0

Pertanyaan bagus! Anda dapat mencoba kode berikut untuk ini. =)

Kode ini menggunakan ast.literal_eval () untuk menemukan tipe data dari input ( age). Kemudian mengikuti algoritma berikut:

  1. Minta pengguna untuk memasukkan / nya age.

    1.1. Jika ageini floatatau inttipe data:

    • Periksa apakah age>=18. Jika age>=18, cetak output yang sesuai dan keluar.

    • Periksa apakah 0<age<18. Jika 0<age<18, cetak output yang sesuai dan keluar.

    • Jika age<=0, minta pengguna untuk memasukkan nomor yang valid untuk umur lagi, ( yaitu kembali ke langkah 1.)

    1.2. Jika agebukan floatatau inttipe data, maka mintalah pengguna untuk memasukkan kembali umurnya ( yaitu kembali ke langkah 1.)

Ini kodenya.

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

Anda selalu dapat menerapkan logika if-else sederhana dan menambahkan satu lagi iflogika ke kode Anda bersama dengan satu 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

Ini akan menjadi toilet tanpa batas dan Anda akan diminta memasuki usia, tanpa batas.

Ep1c1aN
sumber
Ini tidak benar-benar menjawab pertanyaan. Pertanyaannya adalah tentang mendapatkan input pengguna sampai mereka memberikan respons yang valid, bukan tanpa batas waktu .
Georgy
-1

Anda dapat menulis logika yang lebih umum untuk memungkinkan pengguna untuk memasukkan hanya beberapa kali tertentu, karena kasus penggunaan yang sama muncul di banyak aplikasi dunia nyata.

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
sumber
1
Anda lupa untuk meningkatkan nilai iCount setelah setiap loop
Hoai-Thu Vuong
-1

Anda dapat membuat pernyataan input sementara True loop sehingga berulang kali meminta input pengguna dan kemudian istirahat loop itu jika pengguna memasukkan respons yang Anda inginkan. Dan Anda dapat menggunakan blok coba dan kecuali untuk menangani respons yang tidak valid.

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

Variabel var hanya agar jika pengguna memasukkan string, bukan bilangan bulat, program tidak akan mengembalikan "Anda tidak dapat memilih di Amerika Serikat."

pengguna9142415
sumber
-1

Gunakan pernyataan "sementara" sampai pengguna memasukkan nilai sebenarnya dan jika nilai input bukan angka atau itu adalah nilai nol lewati saja dan cobalah untuk bertanya lagi dan seterusnya. Sebagai contoh, saya mencoba menjawab pertanyaan Anda. Jika kita mengira usia kita adalah antara 1 dan 150 maka nilai input diterima, kalau tidak itu nilai yang salah. Untuk mengakhiri program, pengguna dapat menggunakan kunci 0 dan memasukkannya sebagai nilai.

Catatan: Baca komentar di atas kode.

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

Satu lagi solusi untuk menggunakan validasi input menggunakan validasi rentang ValidationErrordan pilihan (opsional) untuk input integer:

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

Pemakaian:

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)

Keluaran:

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

Berikut ini adalah solusi yang lebih bersih dan lebih umum yang menghindari berulang jika / blok lain: menulis fungsi yang mengambil (Kesalahan, prompt kesalahan) berpasangan dalam kamus dan melakukan semua pemeriksaan nilai Anda dengan pernyataan.

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

Pemakaian:

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

Input pengguna persisten menggunakan fungsi rekursif :

Tali

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

name = askName()

Bilangan bulat

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

age = askAge()

dan akhirnya, persyaratan pertanyaan:

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

Solusi sederhana adalah:

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

Penjelasan kode di atas: Agar usia yang valid, itu harus positif dan tidak boleh lebih dari usia fisik normal, misalnya misalnya usia maksimum adalah 120.

Kemudian kita dapat menanyakan usia pengguna dan jika input usia negatif atau lebih dari 120, kami menganggapnya sebagai input yang tidak valid dan meminta pengguna untuk mencoba lagi.

Setelah input yang valid dimasukkan, kami melakukan pemeriksaan (menggunakan pernyataan if-else bersarang) apakah usianya> = 18 atau sebaliknya dan mencetak pesan apakah pengguna berhak memilih

Rohail
sumber
"Silakan masukkan umur Anda: dickety six" ': crash yang sama seperti yang dinyatakan dalam pertanyaan ...
BDL
-2

mengambil input sebagai string dan menggunakan isdigit () untuk memeriksa input hanya memiliki digit, tidak kosong, tidak boleh -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
sumber
Itu juga tidak menjawab pertanyaan.
Georgy