ユーザー入力を受け入れるプログラムを作成しています。
#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.")
ユーザーが意味のあるデータを入力する限り、プログラムは期待どおりに動作します。
Please enter your age: 23
You are able to vote in the United States!
ただし、ユーザーが無効なデータを入力すると失敗します。
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'
クラッシュするのではなく、プログラムに再度入力を求めてほしいです。次のようにします。
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
クラッシュしたり無効な値 (例)を受け入れたりするのではなく、有効な入力を求めるにはどうすればよいですか?
ベストアンサー1
これを実現する最も簡単な方法は、input
メソッドをwhileループ内に置くことです。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
例外処理とカスタム検証を組み合わせる
上記の両方の手法を 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
が、同じことを繰り返さないinput
ソフトウェア開発の原則。これにより、システムにバグが発生する可能性が高くなります。に変更して 2.7 にバックポートしたいが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
。「1000 回連続で間違いを犯すような愚か者はいない」と思うかもしれませんが、それは愚か者の創意工夫を過小評価しています。