ものづくりブログ

ゼロからスタートして何かを作って世に出すまでのことを書くブログです.

Python学習用課題に取り組んでみた(2次回答)

こんにちは。

前回に引き続きPython学習用の課題についての記事です。

添削してもらった結果、得たフィードバックは下記のようなものでした。


  • 不正な入力が来た時にプログラムが止まらないよう例外処理を使ったほうが良い
  • 日付のエラー判定はdateクラスを使うと良い

ふむ。



というわけで書いたプログラムが以下のとおりです。

# -*- coding: utf-8 -*-

from datetime import date

#社員情報クラス
class employee:

    def __init__(self,id,name,bd,salary):
        self.id = id
        self.name = name
        self.bd = bd
        self.salary = salary


#社員登録
def register(employeeid):

    print "名前を入力してください"
    tempname = raw_input()
    while(1):
        print "生年月日を入力してください(yyyy/mm/ddまたはyyyymmddの形で)"
        tempbd = raw_input()
        bdlist = tempbd.split("/")
        bd = []
        if len(bdlist) != 3:
            if tempbd.isdigit() and len(tempbd) == 8:
                try:
                    bd = date(int(tempbd[:-4]),int(tempbd[-4:-2]),int(tempbd[-2:]))
                except:
                    print"不正な値です。"
                    continue
                break
            else:
                print "不正な値です。再度入力してください。"
        else:
            try:
                bd = date(int(bdlist[0]),int(bdlist[1]),int(bdlist[2]))
            except:
                print "不正な値です。"
                continue
            break

    while(1):

        print "月額の給与を入力してください(8桁まで)"
        tempstrsalary = raw_input()
        try:
            tempintsalary = int(tempstrsalary)
        except:
            print "不正な値です。"
            continue
        if tempintsalary > 0 and tempintsalary < 100000000:
            break
        else:
            print "不正な値です。"

    EmployeeList.append(employee(employeeid,tempname,bd,tempintsalary))

    employeeidtemp = employeeid + 1

    return(employeeidtemp)

#社員情報照会
def intro():

    print "*****************************"
    print "ID   名前       生年月日     給与"
    print "*****************************"
    for i in EmployeeList:
        string = str(i.id) + "   " + i.name + "    " + str(i.bd.year) + "年" + str(i.bd.month) +"月" + str(i.bd.day) + "日" + "      " + '{:,d}'.format(i.salary) + "円"
        print string
    print "*****************************"


#削除
def delete(requiredid):

    j = 0
    if requiredid == 0:
        print "削除する社員のIDを入力してください"
        deleteid = input()

    else:
        deleteid = requiredid

    for deletenum in EmployeeList:
        if deletenum.id == deleteid:
            deletedList = EmployeeList.pop(j)
            if requiredid == 0:
                print "%sを削除しました" % deletedList.name
            break
        j = j + 1
    print "該当するIDの社員情報がありませんでした"

#編集
def edit():

    print "更新したい社員のIDを入力してください"
    editid = input()


#削除関数と登録関数により編集を実施
    delete(editid)
    register(editid)




if __name__ == "__main__":

    employeeid = 1  #社員IDを連番にするための変数の初期値
    EmployeeList = []   #社員リスト

    while(1):
        print "<MENU>\n"
        print "========================"
        print "1. 登録"
        print "2. 紹介"
        print "3. 削除"
        print "4. 更新"
        print "========================"

        select = raw_input("実行する処理を半角数字で入力")

        if select == "1":
            employeeid = register(employeeid)
        elif select == "2":
            intro()
        elif select == "3":
            delete(0)
        elif select == "4":
            edit()
        else:
            print "無効なコマンドです。"

1箇所ずつ順に見ていきます。
まずはdateクラスを使うためにimportします。

from datetime import date

次に実際にdateクラスのインスタンスを作成し、代入するところを見てみます。

        tempbd = raw_input()
        bdlist = tempbd.split("/")
        bd = []
        if len(bdlist) != 3:
            if tempbd.isdigit() and len(tempbd) == 8:
                try:
                    bd = date(int(tempbd[:-4]),int(tempbd[-4:-2]),int(tempbd[-2:]))
                except:
                    print"不正な値です。"
                    continue
                break
            else:
                print "不正な値です。再度入力してください。"

ここは添削されたところではないのですが、日付の入力はyyyy/mm/ddだけでなく、yyyymmddでも良いようにすると仕様書にあったため実装した箇所です。
if文により、ユーザが入力した文字列を/で区切った時に3個の要素にならないかを見た後(if len(bdlist) != 3:)、それがTrueだった時(3個でない時)にその文字列が全て数字に変更可能(isdigit())で、かつ8桁であることを判定しています(if tempbd.isdigit() and len(tempbd) == 8:)。
もしこれがTrueなら下4桁を削った値を年、その次の2桁を月、そして最後の2桁を日としてbdという変数に代入しています[2]。
ここで登場するのがdateクラスです。
bdにはdateクラス形式で代入しているので、無効な日付であればエラーを返してくれます。
単純にエラーを返すだけではそこで処理が止まってしまうため、例外処理(try,exception)を使用しました[3]。



次はユーザが入力した文字列を/で区切った時に3個の要素になる場合の処理を見てみます。

        else:
            try:
                bd = date(int(bdlist[0]),int(bdlist[1]),int(bdlist[2]))
            except:
                print "不正な値です。"
                continue
            break

こちらも先ほどと同様にdateクラスを使用するように改造しました。
また、例外処理を用いることで予期せぬ入力にも対応するようにしました。



次はもう1箇所例外処理を導入した箇所です。

        tempstrsalary = raw_input()
        try:
            tempintsalary = int(tempstrsalary)
        except:
            print "不正な値です。"
            continue
        if tempintsalary > 0 and tempintsalary < 100000000:
            break
        else:
            print "不正な値です。"

給与を入力する箇所です。ここでは例外処理を使うこと自体は生年月日の箇所と同じですが、今回はdateのようなクラスが存在しないため、値が正しいかどうかを判断する必要があります。
そこで、try以下で処理を行った後、エラーが無かった場合もif文を使用して0以下でないか、また8桁を超えていないかを判定しています。



以上が添削を受けて私が修正した部分です。



参考文献
[1]Python式日本語版「8.1. datetime — 基本的な日付型および時間型」(http://docs.python.jp/2/library/datetime.html)
[2]PythonWEB「スライスを使った部分文字列の取得」(http://www.pythonweb.jp/tutorial/string/index11.html)
[3]python-izm「例外処理」(http://www.python-izm.com/contents/basis/exception.shtml)