ขอให้ผู้ใช้ป้อนข้อมูลจนกว่าพวกเขาจะให้การตอบสนองที่ถูกต้อง


562

ฉันกำลังเขียนโปรแกรมที่รับอินพุตจากผู้ใช้

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

โปรแกรมทำงานได้ตามที่คาดหวังตราบใดที่ผู้ใช้ป้อนข้อมูลที่มีความหมาย

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

แต่จะล้มเหลวหากผู้ใช้ป้อนข้อมูลที่ไม่ถูกต้อง:

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'

แทนที่จะหยุดทำงานฉันต้องการให้โปรแกรมขออินพุตอีกครั้ง แบบนี้:

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!

ฉันจะทำให้โปรแกรมขออินพุตที่ถูกต้องแทนที่จะหยุดทำงานเมื่อมีการป้อนข้อมูลที่ไม่ใช่ข้อมูลที่มีความรู้สึกได้อย่างไร

ฉันจะปฏิเสธค่าเช่น-1ซึ่งถูกต้องintแต่ไร้สาระในบริบทนี้ได้อย่างไร

คำตอบ:


704

วิธีที่ง่ายที่สุดในการทำเช่นนี้คือการใส่inputวิธีลงในขณะที่ ใช้continueเมื่อคุณได้รับอินพุตที่ไม่ดีและbreakออกจากลูปเมื่อคุณพอใจ

เมื่อข้อมูลที่คุณป้อนอาจมีข้อยกเว้น

ใช้tryและexceptเพื่อตรวจหาเมื่อผู้ใช้ป้อนข้อมูลที่ไม่สามารถแยกวิเคราะห์ได้

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

การใช้กฎการตรวจสอบของคุณเอง

หากคุณต้องการปฏิเสธค่าที่ Python สามารถแยกวิเคราะห์ได้สำเร็จคุณสามารถเพิ่มตรรกะการตรวจสอบของคุณเอง

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

การรวมการจัดการข้อยกเว้นและการตรวจสอบที่กำหนดเอง

เทคนิคทั้งสองข้างต้นสามารถรวมกันเป็นวงเดียว

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

การห่อหุ้มมันทั้งหมดในฟังก์ชั่น

หากคุณต้องการถามผู้ใช้ของคุณเกี่ยวกับค่าที่แตกต่างกันจำนวนมากมันอาจมีประโยชน์ที่จะนำโค้ดนี้ไปใช้ในฟังก์ชั่นดังนั้นคุณไม่จำเป็นต้องพิมพ์ซ้ำทุกครั้ง

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

วางมันทั้งหมดเข้าด้วยกัน

คุณสามารถขยายแนวคิดนี้เพื่อสร้างฟังก์ชันอินพุตทั่วไป:

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

ด้วยการใช้งานเช่น:

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

ข้อผิดพลาดทั่วไปและทำไมคุณควรหลีกเลี่ยงพวกเขา

การใช้inputคำสั่งที่ซ้ำซ้อน

วิธีการนี้ใช้งานได้ แต่โดยทั่วไปถือว่าเป็นรูปแบบที่ไม่ดี

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

มันอาจดูน่าสนใจในตอนแรกเพราะมันสั้นกว่าwhile Trueวิธีการ แต่มันละเมิดหลักการDon't Repeat Yourselfของการพัฒนาซอฟต์แวร์ สิ่งนี้จะเพิ่มโอกาสในการเกิดข้อบกพร่องในระบบของคุณ ถ้าคุณต้องการเปลี่ยนกลับเป็น 2.7 ด้วยการเปลี่ยนinputเป็นraw_inputแต่บังเอิญเปลี่ยนเฉพาะอันแรกinputด้านบน? มันเป็นSyntaxErrorเพียงแค่รอที่จะเกิดขึ้น

การเรียกซ้ำจะพัดสแต็คของคุณ

หากคุณเพิ่งเรียนรู้เกี่ยวกับการเรียกซ้ำคุณอาจถูกล่อลวงให้ใช้มันget_non_negative_intเพื่อให้คุณสามารถกำจัดลูปในขณะที่

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

ปรากฏปรับการทำงานมากที่สุดของเวลา RuntimeError: maximum recursion depth exceededแต่ถ้าผู้ใช้ป้อนข้อมูลที่ไม่ถูกต้องครั้งพอสคริปต์ที่จะยุติด้วยนี้ คุณอาจคิดว่า "ไม่มีคนโง่ที่จะทำผิดพลาด 1,000 ครั้งติดต่อกัน" แต่คุณประเมินความฉลาดของคนโง่ต่ำไป!


53
สนุกกับการอ่านมันด้วยตัวอย่างมากมายรุ่งโรจน์ บทเรียนที่มีการประเมินต่ำ: "อย่าประมาทความฉลาดของคนเขลา!"
vpibano

3
ไม่เพียง แต่ฉันจะได้ถอนรากถอนโคนทั้ง Q&A ต่อไปเพราะมันยอดเยี่ยม แต่คุณได้ปิดผนึกข้อตกลงกับ "dickety six" ทำได้ดีมาก @ เควิน
erekalper

1
อย่าประเมินความฉลาดของคนโง่ ... และผู้โจมตีที่ฉลาด การโจมตีแบบ DOS นั้นง่ายที่สุดสำหรับสิ่งนี้ แต่คนอื่นอาจทำได้
โซโลมอน Ucko

เราสามารถใช้โอเปอเรเตอร์ "walrus" ใหม่แทนอินพุตซ้ำซ้อนได้หรือไม่? มันเป็นสไตล์ที่ไม่ดีเลยเหรอ?
J Arun Mani

1
@JArunMani ฉันไม่คิดว่ามันจะเป็นรูปแบบที่ไม่ดี แต่อาจอ่านน้อยกว่านี้เล็กน้อย แน่นอนคุณจะมีเพียงหนึ่งinputต่อวงและวงจะสั้นมาก แต่เงื่อนไขอาจจะยาว ...
Tomerikoo

39

เหตุใดคุณจึงต้องทำ a while Trueแล้วแยกวงนี้ออกไปในขณะที่คุณสามารถใส่ข้อกำหนดของคุณลงในคำสั่ง while ได้เนื่องจากสิ่งที่คุณต้องการคือหยุดเมื่อคุณมีอายุมากขึ้น

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

สิ่งนี้จะส่งผลต่อไปนี้:

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.

สิ่งนี้จะใช้งานได้เนื่องจากอายุจะไม่มีค่าที่ไม่สมเหตุสมผลและรหัสจะเป็นไปตามตรรกะของ "กระบวนการทางธุรกิจ" ของคุณ


22

แม้ว่าคำตอบที่ได้รับการยอมรับเป็นที่น่าอัศจรรย์ ฉันต้องการแบ่งปันแฮ็คอย่างรวดเร็วสำหรับปัญหานี้ (สิ่งนี้จะดูแลปัญหาเรื่องอายุที่ไม่ดีเช่นกัน)

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 รหัสนี้ใช้สำหรับ python 3.x


1
โปรดทราบว่ารหัสนี้เป็นแบบเรียกซ้ำ แต่การเรียกซ้ำไม่จำเป็นที่นี่และอย่างที่เควินบอกว่ามันสามารถทำให้สแตกของคุณได้
PM 2Ring

2
@ PM2Ring - คุณพูดถูก แต่จุดประสงค์ของฉันที่นี่เพียงเพื่อแสดงให้เห็นว่า "การลัดวงจรในระยะสั้น" สามารถลดจำนวนชิ้นส่วนที่ยาว (บรรเจิด) ได้อย่างไร
aaveg

11
ทำไมคุณต้องกำหนดแลมบ์ดาให้กับตัวแปรให้ใช้defแทน def f(age):ชัดเจนยิ่งกว่าf = lambda age:
GP89

3
ในบางกรณีคุณอาจต้องการอายุเพียงครั้งเดียวแล้วไม่มีการใช้งานฟังก์ชั่นนั้น บางคนอาจต้องการใช้ฟังก์ชั่นและทิ้งมันไปหลังจากงานเสร็จสิ้น นอกจากนี้อาจไม่ใช่วิธีที่ดีที่สุด แต่แน่นอนว่าเป็นวิธีที่แตกต่างในการทำเช่นนั้น (ซึ่งเป็นจุดประสงค์ของการแก้ปัญหาของฉัน)
aaveg

@ aaveg คุณจะเปลี่ยนรหัสนี้อย่างไรเพื่อบันทึกอายุที่ผู้ใช้ให้ไว้?
Tytire Recubans

12

ดังนั้นฉันจึงยุ่งกับสิ่งที่คล้ายกับสิ่งนี้เมื่อเร็ว ๆ นี้และฉันมาพร้อมกับวิธีแก้ไขปัญหาต่อไปนี้ซึ่งใช้วิธีรับอินพุตที่ปฏิเสธขยะก่อนที่จะตรวจสอบด้วยวิธีตรรกะ

read_single_keypress()มารยาท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()

คุณสามารถค้นหาโมดูลที่สมบูรณ์ที่นี่

ตัวอย่าง:

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

โปรดทราบว่าลักษณะของการใช้งานนี้คือการปิด stdin ทันทีที่อ่านสิ่งที่ไม่ใช่ตัวเลข ฉันไม่ได้กดปุ่มaแต่ฉันต้องกดหมายเลข

คุณสามารถรวมสิ่งนี้เข้ากับthismany()ฟังก์ชั่นในโมดูลเดียวกันเพื่ออนุญาตให้พูดสามหลักเท่านั้น


12

วิธีการใช้งานหรือ " ดูแม่ไม่มีลูป! ":

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

หรือหากคุณต้องการให้มีข้อความ "อินพุตไม่ถูกต้อง" แยกจากพรอมต์อินพุตเหมือนกับคำตอบอื่น ๆ :

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

มันทำงานยังไง?

  1. prompts = chain(["Enter a number: "], repeat("Not a number! Try again: "))
    การรวมกันของitertools.chainและitertools.repeatจะสร้างตัววนซ้ำซึ่งจะให้ผลสตริง"Enter a number: "หนึ่งครั้งและ"Not a number! Try again: "จำนวนอนันต์:
    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)- ที่นี่mapจะใช้promptsสตริงทั้งหมดจากขั้นตอนก่อนหน้ากับinputฟังก์ชั่น เช่น:
    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. เราใช้filterและstr.isdigitกรองสตริงที่มีตัวเลขเท่านั้น:
    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...
    
    nextและเพื่อให้ได้เพียงตัวเลขเพียงครั้งแรกที่ใช้สตริงเรา

กฎการตรวจสอบอื่น ๆ :

  1. วิธีการสตริง:แน่นอนคุณสามารถใช้วิธีการสตริงอื่น ๆ ที่str.isalphaจะได้รับเพียงสตริงตัวอักษรหรือstr.isupperเพื่อให้ได้ตัวพิมพ์ใหญ่เท่านั้น ดูเอกสารสำหรับรายการทั้งหมด

  2. การทดสอบความเป็นสมาชิก:
    มีหลายวิธีในการดำเนินการ หนึ่งในนั้นคือโดยใช้__contains__วิธีการ:

    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. การเปรียบเทียบตัวเลข:
    มีวิธีการเปรียบเทียบที่มีประโยชน์ซึ่งเราสามารถใช้ได้ที่นี่ ตัวอย่างเช่นสำหรับ__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
    

    หรือถ้าคุณไม่ชอบการใช้วิธีการ dunder (dunder = double-underscore) คุณสามารถกำหนดฟังก์ชั่นของคุณเองหรือใช้สิ่งที่อยู่ในoperatorโมดูล

  4. เส้นทางที่อยู่: ที่
    นี่หนึ่งสามารถใช้pathlibไลบรารีและPath.existsวิธีการ:

    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
    

จำนวนครั้งที่ จำกัด :

หากคุณไม่ต้องการที่จะทรมานผู้ใช้โดยขอให้เขาบางสิ่งบางจำนวนอนันต์ครั้งคุณสามารถระบุขีด จำกัด itertools.repeatในการเรียกของ สิ่งนี้สามารถใช้ร่วมกับการให้ค่าเริ่มต้นกับnextฟังก์ชั่น:

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!

การประมวลผลข้อมูลอินพุตล่วงหน้า:

บางครั้งเราไม่ต้องการปฏิเสธอินพุตหากผู้ใช้ป้อนโดยไม่ตั้งใจในCAPSหรือมีช่องว่างในการเริ่มต้นหรือสิ้นสุดของสตริง ในการพิจารณาความผิดพลาดง่าย ๆ เหล่านี้เราสามารถประมวลผลข้อมูลอินพุตล่วงหน้าได้โดยการใช้str.lowerและstr.stripวิธีการ ตัวอย่างเช่นสำหรับกรณีของการเป็นสมาชิกการทดสอบรหัสจะมีลักษณะเช่นนี้:

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

ในกรณีที่เมื่อคุณมีฟังก์ชั่นมากมายที่จะใช้สำหรับการประมวลผลเบื้องต้นก็อาจจะง่ายต่อการใช้ฟังก์ชั่นการแสดงองค์ประกอบของฟังก์ชั่น ตัวอย่างเช่นใช้จากที่นี่ :

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

การรวมกฎการตรวจสอบ:

ตัวอย่างเช่นกรณีที่เมื่อโปรแกรมถามอายุระหว่าง 1 ถึง 120 คนหนึ่งสามารถเพิ่มอีก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)

แต่ในกรณีที่มีกฎระเบียบมากจะดีกว่าที่จะใช้ฟังก์ชั่นการดำเนินการร่วมตรรกะ ในตัวอย่างต่อไปนี้ฉันจะใช้พร้อมจากที่นี่ :

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

แต่ถ้ามีคนต้องการที่ข้อความที่กำหนดเองสำหรับแต่ละกรณีล้มเหลวแล้วฉันกลัวไม่มีสวยวิธีการทำงาน หรืออย่างน้อยฉันก็หาไม่เจอ


ช่างเป็นคำตอบที่ละเอียดและยอดเยี่ยมคำอธิบายที่ดีมาก
Locane

การใช้สไตล์ของคุณจะทำให้การลบ whitespace และการป้อนข้อมูลสำหรับการทดสอบการเป็นสมาชิกลดลงได้อย่างไร ฉันไม่ต้องการสร้างชุดที่ต้องมีตัวอย่างทั้งตัวใหญ่และตัวเล็ก ฉันต้องการอนุญาตข้อผิดพลาดในการป้อนข้อมูลของช่องว่าง
Austin

1
@ ออสตินฉันเพิ่มส่วนใหม่ในการประมวลผลล่วงหน้า ลองดูสิ.
จอร์จ

นั่นทำให้ฉันนึกถึง ReactiveX แต่บางทีนั่นอาจเป็นแรงบันดาลใจจากภาษาที่ใช้งานได้ตั้งแต่แรก
Mateen Ulhaq

8

ใช้การคลิก :

Clickเป็นไลบรารี่สำหรับอินเตอร์เฟสบรรทัดคำสั่งและมีฟังก์ชันสำหรับถามการตอบสนองที่ถูกต้องจากผู้ใช้

ตัวอย่างง่ายๆ:

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

สังเกตว่ามันแปลงค่าสตริงเป็นทศนิยมโดยอัตโนมัติอย่างไร

ตรวจสอบว่าค่าอยู่ในช่วง:

มีประเภทที่กำหนดเองให้ ในการรับตัวเลขในช่วงเฉพาะเราสามารถใช้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

นอกจากนี้เรายังสามารถระบุเพียงหนึ่งในข้อ จำกัดminหรือ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

การทดสอบสมาชิก:

ใช้click.Choiceประเภท โดยค่าเริ่มต้นการตรวจสอบนี้จะคำนึงถึงขนาดตัวพิมพ์

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

การทำงานกับพา ธ และไฟล์:

ใช้click.Pathประเภทที่เราสามารถตรวจสอบเส้นทางที่มีอยู่และแก้ไขพวกเขา:

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

การอ่านและการเขียนไฟล์สามารถทำได้โดย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!

ตัวอย่างอื่น ๆ :

การยืนยันรหัสผ่าน:

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

ค่าเริ่มต้น:

ในกรณีนี้เพียงกดEnter(หรือคีย์ใด ๆ ที่คุณใช้) โดยไม่ป้อนค่าจะให้ค่าเริ่มต้น:

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

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

2

สร้างจากคำแนะนำที่ยอดเยี่ยมของ Daniel Q และ Patrick Artner นี่เป็นวิธีการแก้ปัญหาที่กว้างกว่าเดิม

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

ฉันเลือกใช้อย่างชัดเจนifและraiseแถลงการณ์แทนที่จะเป็นassertเพราะการตรวจสอบการยืนยันอาจถูกปิดในขณะที่การตรวจสอบความถูกต้องควรจะให้ความแข็งแกร่งเสมอ

สิ่งนี้อาจถูกใช้เพื่อรับอินพุตประเภทต่าง ๆ พร้อมเงื่อนไขการตรวจสอบที่แตกต่างกัน ตัวอย่างเช่น:

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

หรือเพื่อตอบคำถามเดิม:

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

1

ลองอันนี้:-

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

0

ในขณะที่try/ บล็อกจะทำงานเป็นวิธีที่เร็วและทำความสะอาดเพื่อทํางานนี้จะมีการใช้งานexceptstr.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.")

0

คำถามที่ดี! คุณสามารถลองใช้รหัสต่อไปนี้สำหรับสิ่งนี้ =)

รหัสนี้ใช้ast.literal_eval ()เพื่อค้นหาชนิดข้อมูลของอินพุต ( age) จากนั้นจะทำตามอัลกอริทึมต่อไปนี้:

  1. ageขอให้ผู้ใช้ต้องใส่เธอ / เขา

    1.1 ถ้าageเป็นfloatหรือintชนิดข้อมูล:

    • age>=18ตรวจสอบว่า ถ้าage>=18พิมพ์ออกและออกที่เหมาะสม

    • 0<age<18ตรวจสอบว่า ถ้า0<age<18พิมพ์ออกและออกที่เหมาะสม

    • หากage<=0ขอให้ผู้ใช้ป้อนหมายเลขที่ถูกต้องสำหรับอายุอีกครั้ง ( เช่นกลับไปที่ขั้นตอนที่ 1)

    1.2 หากageไม่ใช่floatหรือintชนิดข้อมูลให้ผู้ใช้ป้อนอายุของเขาอีกครั้ง ( เช่นกลับไปที่ขั้นตอนที่ 1)

นี่คือรหัส

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

0

คุณสามารถใช้ตรรกะ if-else แบบง่ายได้ตลอดเวลาและเพิ่มifตรรกะอีกหนึ่งรหัสกับforวงของคุณ

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

นี่จะเป็นลูไม่สิ้นสุดและคุณจะถูกขอให้เข้าสู่ยุคนี้อย่างไม่มีกำหนด


นี่ไม่ได้ตอบคำถามจริงๆ คำถามที่ได้รับเกี่ยวกับการป้อนข้อมูลของผู้ใช้จนกว่าพวกเขาให้การตอบสนองที่ถูกต้องไม่ได้ไปเรื่อย ๆ
Georgy

-1

คุณสามารถเขียนตรรกะทั่วไปเพิ่มเติมเพื่อให้ผู้ใช้ป้อนจำนวนครั้งที่เฉพาะเจาะจงเท่านั้นเนื่องจากกรณีการใช้งานเดียวกันนั้นเกิดขึ้นในแอปพลิเคชันในโลกแห่งความเป็นจริงมากมาย

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.

1
คุณลืมที่จะเพิ่มค่า iCount หลังจากแต่ละวง
Hoai-Thu Vuong

-1

คุณสามารถทำให้คำสั่งการป้อนข้อมูลในขณะที่ทรูลูปดังนั้นมันซ้ำ ๆ ถามผู้ใช้และจากนั้นแบ่งลูปนั้นถ้าผู้ใช้เข้าสู่การตอบสนองที่คุณต้องการ และคุณสามารถใช้ลองและยกเว้นบล็อกเพื่อจัดการกับการตอบกลับที่ไม่ถูกต้อง

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

ตัวแปร var เป็นเพียงเพื่อที่ว่าหากผู้ใช้ป้อนสตริงแทนจำนวนเต็มโปรแกรมจะไม่ส่งคืน "คุณไม่สามารถลงคะแนนในสหรัฐอเมริกาได้"


-1

ใช้คำสั่ง "while" จนกระทั่งผู้ใช้ป้อนค่าที่แท้จริงและหากค่าที่ป้อนไม่ใช่ตัวเลขหรือเป็นค่าว่างให้ข้ามและลองถามอีกครั้ง ในตัวอย่างฉันพยายามตอบคำถามของคุณอย่างแท้จริง หากเราสมมติว่าอายุของเราอยู่ระหว่าง 1 ถึง 150 ดังนั้นค่าอินพุตที่ยอมรับจะเป็นค่าที่ไม่ถูกต้อง สำหรับการยกเลิกโปรแกรมผู้ใช้สามารถใช้รหัส 0 และป้อนเป็นค่า

หมายเหตุ: อ่านความคิดเห็นด้านบนของรหัส

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

-1

อีกหนึ่งโซลูชั่นสำหรับการใช้การตรวจสอบความถูกต้องของอินพุตโดยใช้การตรวจสอบช่วงที่กำหนดเองValidationErrorและ (เป็นทางเลือก) สำหรับอินพุตจำนวนเต็ม:

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

การใช้งาน:

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)

เอาท์พุท:

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

-1

ต่อไปนี้เป็นวิธีแก้ปัญหาทั่วไปที่สะอาดกว่าและหลีกเลี่ยงการบล็อกซ้ำถ้า / else: เขียนฟังก์ชั่นที่จับคู่ (เกิดข้อผิดพลาดแจ้งข้อผิดพลาด) ในพจนานุกรมและทำการตรวจสอบค่าทั้งหมดด้วยการยืนยัน

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

การใช้งาน:

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

-1

อินพุตผู้ใช้ถาวรโดยใช้ฟังก์ชั่นวนซ้ำ :

เชือก

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

name = askName()

จำนวนเต็ม

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

age = askAge()

และในที่สุดคำถามที่ต้องการ:

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)

-2

ทางออกที่ง่ายจะเป็น:

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

คำอธิบายของรหัสข้างต้น: เพื่อให้อายุที่ถูกต้องมันควรจะเป็นบวกและไม่ควรเกินอายุทางกายภาพปกติพูดเช่นอายุสูงสุดคือ 120

จากนั้นเราสามารถขออายุผู้ใช้และหากการป้อนข้อมูลอายุเป็นลบหรือมากกว่า 120 เราจะถือว่าการป้อนข้อมูลนั้นไม่ถูกต้องและขอให้ผู้ใช้ลองอีกครั้ง

เมื่อป้อนข้อมูลที่ถูกต้องเราจะทำการตรวจสอบ (โดยใช้คำสั่ง if-else แบบซ้อนกัน) ไม่ว่าจะเป็นอายุ> = 18 หรือในทางกลับกันและพิมพ์ข้อความว่าผู้ใช้มีสิทธิ์ลงคะแนนหรือไม่


"กรุณากรอกอายุของคุณ: dickety หก": ความผิดพลาดเช่นเดียวกับที่ระบุไว้ในคำถาม ...
BDL

-2

ใช้อินพุตเป็นสตริงและใช้ isdigit () เพื่อตรวจสอบอินพุตเท่านั้นที่มีตัวเลขไม่ว่างเปล่าไม่สามารถ -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


มันยังไม่ตอบคำถาม
Georgy
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.