見出し画像

『退屈なことはPythonにやらせよう』8章 入力検証のらくがき帳📝

こんにちは。aliceです。
昨日は久しぶりに梨を食べました。甘くてみずみずしくておいしかったです。食べ物から季節を感じています。

なし

今回は『退屈なことはPythonにやらせよう』第8章 入力検証のらくがき帳です。

入力検証とは、input関数などでユーザーが入力した値が正しいかどうか調べることです。
あまりinput関数を使うようなコードを書いたことがないのですが、やってみたら普通に楽しかったです。


前回のらくがき帳📝


では、以下らくがきです。


8章 英語版🔤


GitHub💖


8.1 PyInputPlusモジュール


PyInputPlusモジュール、はじめて知った。

PyInputPlusモジュールは数値や日付、メールアドレスなどのいくつかのデータ型についてinput()に似た関数を提供します。

import pyinputplus as pyip

response = pyip.inputNum('数字を入力してください')
print(response)


inputNum関数の引数
いろいろある。

def inputNum(
        prompt="",  # プロンプト (入力を促す文字列)
        default=None,  # デフォルト値。デフォルト値が指定されている場合、ユーザーが何も入力せずに Enter キーを押した場合にデフォルト値が返されます。
        blank=False,  # 空白の入力を許可するかどうか
        timeout=None,  # タイムアウト時間 (秒)
        limit=None,  # 入力を受け付ける文字数の上限
        strip=None,  # 入力の前後にある空白を削除するかどうか
        allowRegexes=None,  # 正規表現のリスト。これらの正規表現に一致する入力は許可されます。
        blockRegexes=None,  # 正規表現のリスト。これらの正規表現に一致する入力は許可されません。
        applyFunc=None,  # 入力された値に対して呼び出される関数
        postValidateApplyFunc=None,  # 入力された値に対して呼び出される関数
        min=None,  # 最小値
        max=None,  # 最大値
        greaterThan=None,  # 最小値より大きい
        lessThan=None,  # 最大値より小さい
):


引数の位置と何があるかがわからないので、ちょこちょこ調べるスタイル。


8.1.5 inputCustom()に自作の検証関数を渡す

自分で作った関数でも入力検証できるらしい。
へぇぇ。

import pyinputplus as pyip


def adds_up_to_ten(numbers):
    numbersList = list(numbers)
    for i, digit in enumerate(numbersList):
        numbersList[i] = int(digit)
    if sum(numbersList) != 10:
        raise Exception(f'足しても10になりません, {sum(numbersList)}です')
    return int(numbers)  # Return an int form of numbers.


response = pyip.inputCustom(adds_up_to_ten, prompt='合計が10になる数字を入力してください: ')
print(response)


8.3 プロジェクト:掛け算クイズ

せっかくなので2桁の足し算クイズにしてみた。
何気に難しい🥲
頭の運動にはぴったりでした。

こういうところでも正規表現を使えるんですね。

import random
import time

import pyinputplus as pyip

number_of_questions = 5
correct_answers = 0
for question_number in range(number_of_questions):

    num1 = random.randint(11, 99)
    num2 = random.randint(11, 99)
    prompt = f'#{question_number}: {num1} + {num2} = '

    try:
        # 正解は allowRegexes で処理する
        # 不正解は blockRegexes で処理され、カスタムメッセージが表示される
        pyip.inputStr(
            prompt,
            allowRegexes=[f'^{num1 + num2}$'],
            blockRegexes=[('.*', '残念!')],
            timeout=10,
            limit=3
        )
    except pyip.TimeoutException:
        print('時間切れです!')
    except pyip.RetryLimitException:
        print('所定の回数を超えました!')
    else:
        # try ブロックで例外が発生しなかった場合に実行
        print('正解!')
        correct_answers += 1
    time.sleep(1)


こういうのをサクッと考えられたらすごいなと思う。


正規表現を使わないとながーくなるんですね。
(ちょっとルールが変わった気がする💦)

import random
import time

import pyinputplus as pyip


def is_correct_answer(prompt, answer, tries=3, timeout=10):
    for _ in range(tries):
        try:
            user_input = pyip.inputStr(prompt, timeout=timeout)
            if user_input == answer:
                print('正解!')
                return True
            else:
                print('残念!')
        except pyip.TimeoutException:
            print('時間切れです!')
        except pyip.RetryLimitException:
            print('所定の回数を超えました!')

    return False


number_of_questions = 5
correct_answers = 0
for question_number in range(number_of_questions):
    num1 = random.randint(11, 99)
    num2 = random.randint(11, 99)
    answer = str(num1 + num2)
    prompt = f'#{question_number}: {num1} + {num2} = '

    if is_correct_answer(prompt, answer):
        correct_answers += 1

    time.sleep(1)

print(f'Score: {correct_answers} / {number_of_questions}')


8.6 演習プロジェクト

8.6.1 サンドイッチメーカー

ユーザーにサンドイッチの好みを尋ねるプログラムを書きましょう。
PyInputPlusモジュールを使うとさくっと書けるんですね🥪

import pyinputplus as pyip


def sandwich_maker():
    bread = pyip.inputMenu(['小麦', '白パン', '全粒粉', 'ライ麦'], prompt='パンを選んでください:\n', )
    protein = pyip.inputMenu(['チキン', 'ターキー', 'ハム', '豆腐'], prompt='タンパク質の種類を選んでください:\n', )
    cheese = pyip.inputYesNo(prompt='チーズを追加しますか?\n', yesVal='はい', noVal='いいえ',)
    if cheese == 'はい':
        cheese = pyip.inputMenu(['チェダー', 'スイス', 'モッツァレラ'], prompt='チーズを選んでください:\n', )
    else:
        cheese = 'なし'
    tomato = pyip.inputYesNo(prompt='トマトを追加しますか?\n', yesVal='はい', noVal='いいえ', )
    lettuce = pyip.inputYesNo(prompt='レタスを追加しますか?\n', yesVal='はい', noVal='いいえ', )
    mayo = pyip.inputYesNo(prompt='マヨネーズを追加しますか?\n', yesVal='はい', noVal='いいえ', )
    mustard = pyip.inputYesNo(prompt='マスタードを追加しますか?\n', yesVal='はい', noVal='いいえ', )

    count = pyip.inputInt(prompt='サンドイッチの数を入力してください:\n', min=1)

    print(f'注文内容は以下の通りです:\n'
          f'パン: {bread}\n'
          f'タンパク質: {protein}\n'
          f'チーズ: {cheese}\n'
          f'トマト: {tomato}\n'
          f'レタス: {lettuce}\n'
          f'マヨネーズ: {mayo}\n'
          f'マスタード: {mustard}\n'
          f'サンドイッチの数: {count}\n'
          f'ありがとうございました!')


if __name__ == '__main__':
    sandwich_maker()


8.6.2 自作の掛け算クイズ

PyInputPlusモジュールのありがたみを実感するために、PyInputPlusモジュールを使わないで掛け算クイズを書きましょう。

ありがたみをとても実感できたのでChatGPTに書いてもらいました。

import random
import time

number_of_questions = 10
correct_answers = 0

for question_number in range(number_of_questions):
    num1 = random.randint(0, 9)
    num2 = random.randint(0, 9)
    answer = num1 * num2
    prompt = f'#{question_number}: {num1} x {num2} = '

    attempts = 0
    while attempts < 3:
        user_input = input(prompt)
        try:
            user_answer = int(user_input)
        except ValueError:
            print('Invalid input. Please enter a number.')
            continue

        if user_answer == answer:
            print('Correct!')
            correct_answers += 1
            time.sleep(1)  # Brief pause to let user see the result.
            break
        else:
            print('Incorrect!')
            attempts += 1

        if attempts == 3:
            print('Out of tries!')
            time.sleep(1)  # Brief pause before moving to the next question.

print(f'Score: {correct_answers} / {number_of_questions}')


input関数で入力してもらうのも、なかなか楽しいです。

この記事が気に入ったらサポートをしてみませんか?