1. ホーム
  2. python

[解決済み] ユーザーが有効な応答を返すまで入力を求める

2022-03-15 14:48:41

質問

ユーザーからの入力を受け付けるプログラムを書いています。

#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 しかし、この文脈では無意味ではないか?

どのように解決するのですか?

これを実現する最も簡単な方法は input メソッドをwhileループに入れる。使用方法 continue は、悪い入力があったときに、そして break を、満足したらループから抜ける。

入力で例外が発生する可能性がある場合

使用方法 tryexcept を使用して、ユーザーが解析できないデータを入力したことを検出します。

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

例外処理とカスタムバリデーションの組み合わせ

上記の2つのテクニックは、1つのループにまとめることができます。

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 メソッドに違反しますが、これは 同じことを繰り返さない ソフトウェア開発の原則です。そのため、システムにバグが発生する可能性が高くなります。を変更して2.7にバックポートしたい場合はどうでしょうか? input から raw_input が、誤って最初の input 上記の?それは SyntaxError を待つだけです。

再帰はあなたのスタックを吹き飛ばす

再帰について学んだばかりの人は、再帰を利用するために get_non_negative_int で、whileループを破棄することができます。

def get_non_negative_int(prompt):
    try:
        value = int(input(prompt))
    except ValueError:
        print("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 . しかし、あなたは愚か者の工夫を過小評価しているのです!あなたは、quot;どんな愚か者も1000回も連続して間違いを犯すことはないと思うかもしれません。