読者です 読者をやめる 読者になる 読者になる

ものづくりブログ

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

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

Python

こんにちは.

前回の記事で,Python勉強用の課題を掲載しました.今回はそれに対する1回目の私の回答を掲載します.1回目というのは,このあとこの回答を元にエンジニアに添削してもらい,色々と気づきがあったので,あえて最終形をいきなり掲載するのではなく,最初に私が自分で書いたコードを掲載することにしました.

私が1回目に書いたコードは以下のとおりです.

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

#社員情報クラス
class employee:

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

#生年月日クラス
class p_bd:

    def __init__(self,year,month,date):
        self.year = year
        self.month = month
        self.date = date

#社員登録
def register(employeeid):

    print "名前を入力してください"
    tempname = raw_input()
    print "生年月日を入力してください(yyyy/mm/ddの形で)"
    while(1):
        tempbd = raw_input()
        bdlist = tempbd.split("/")
        if len(bdlist) != 3:
            print "不正な値です.再度入力してください"
        elif int(bdlist[0]) > 2015:
            print "不正な値です.再度入力してください"
        elif int(bdlist[1]) > 12 or int(bdlist[1]) < 1:
            print "不正な値です.再度入力してください"
        elif int(bdlist[2]) > 31 or int(bdlist[2]) < 1:
            print "不正な値です.再度入力してください"
        else:
            break
    bdclass = p_bd(bdlist[0],bdlist[1],bdlist[2])

    print "月額の給与を入力してください(8桁まで)"
    while(1):
        tempsalary = raw_input()
        if tempsalary.isdigit():
            tempintsalary = int(tempsalary)
            if tempintsalary <= 99999999:
                break
            else:
                print "桁数が多すぎます.再度入力してください"
        else:
            print "数値ではありません.再度入力してください"

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

    employeeidtemp = employeeid + 1

    return(employeeidtemp)

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

    print "*****************************"
    print "ID   名前       生年月日     給与"
    print "*****************************"
    for i in EmployeeList:
        string = str(i.id) + "   " + i.name + "    " + i.bd.year + "年" + i.bd.month +"月" + i.bd.date + "日" + "      " + '{:,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 "無効なコマンドです。"

順に解説していきます.

以下はC言語でいうところのmain関数に当たる部分です.プログラムはまずここから実行されます.

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 "無効なコマンドです。"

最初に社員IDを表す変数(employeeid)を作成し,そこに初期値1を代入しています.今後社員情報が追加される度にこの変数に1を足していくことで,IDを連番にします.
次にこの社員情報を集めたリストを作成します.最初は空のリストとして作成しています[1].

次にwhileでくくっていますが,条件が(1)になっていますのでこの中身を無限にループします.
whileの中身を見てみましょう.
printを使用してどのようなことができるのかを表示しています.それぞれの処理に1〜4までの番号を振って表示しています.

次に出てくる,raw_inputは,コマンドラインからの入力を受け付ける関数です[2].引数に文字列を渡すと入力欄の前にメッセージを表示することができます(下記).

f:id:daisukekmr:20150222202652p:plain

ここでユーザに1〜4までの数字を入力してもらい,この後のif文で実行する動作を指定しています.1〜4の数字以外のデータが入力した場合は無効なコマンドであることを通知し,再度入力させるようにしています.



次にそれぞれの動作を実行する関数を見ていきます.まずは登録です.

#社員登録
def register(employeeid):

    print "名前を入力してください"
    tempname = raw_input()

関数を定義し,ユーザに社員の名前を入力させています.main関数からこの関数が呼ばれた時に引数として社員IDが渡されています.



次は生年月日の入力です.

    print "生年月日を入力してください(yyyy/mm/ddの形で)"
    while(1):
        tempbd = raw_input()
        bdlist = tempbd.split("/")
        if len(bdlist) != 3:
            print "不正な値です.再度入力してください"
        elif int(bdlist[0]) > 2015:
            print "不正な値です.再度入力してください"
        elif int(bdlist[1]) > 12 or int(bdlist[1]) < 1:
            print "不正な値です.再度入力してください"
        elif int(bdlist[2]) > 31 or int(bdlist[2]) < 1:
            print "不正な値です.再度入力してください"
        else:
            break
    bdclass = p_bd(bdlist[0],bdlist[1],bdlist[2])

生年月日は書式が決まっているので,きちんとそれに沿っているかを判定する必要があります.
入力はyyyy/mm/ddで入力させることにし,まずはきちんとそれに則っているかを判定します.具体的には,"/"で文字列を区切って[3],きちんと3つの文字列に区切られているかを判定します.
もしこれが問題なければ次に区切られた文字列の1番目,すなわち年が未来になっていないかを判定します.ここは1900年以前など過去でも現実的でない数値の場合もエラーと判定してしまっても良いかもしれません.
次に月と日についても同様の判定を行います.実際は2/31などありえないのですが,その辺りを気にするとうるう年を気にする必要が出てきて,かなり煩雑になるので一旦無視しています.
全て問題なければbreakによって繰り返しから脱し,次の行で年月日を変数に代入します.
後ほど述べますが,生年月日は年と月と日をそれぞれわけてクラスとして定義していますので,その形式で代入しています.



次は給与の入力です.

    print "月額の給与を入力してください(8桁まで)"
    while(1):
        tempsalary = raw_input()
        if tempsalary.isdigit():
            tempintsalary = int(tempsalary)
            if tempintsalary <= 99999999:
                break
            else:
                print "桁数が多すぎます.再度入力してください"
        else:
            print "数値ではありません.再度入力してください"

給与は数値だけで入力してもあり,8桁以内と指定しています.
文字列を整数型に変更するにはint()関数を使用すればよいのですが[4],整数に変更できない文字列だった場合エラーになってしまうため,事前に整数に変換可能な文字列かどうかを判定するため,isdigit()関数を使用しています[5].
こうして整数型に変更したら,あとは桁数の判定を行い,問題があれば再入力を促します.



次はこれまでユーザに入力させた情報を社員情報としてリストに追加します.

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

    employeeidtemp = employeeid + 1

    return(employeeidtemp)

今回のプログラムでは,社員情報をクラス化しています.リストは複数の社員情報をそれぞれクラスとして保持します.
上記では,EmployeeListというリストに,employeeクラスの形式で社員情報を保持しています.このクラスの定義と中身については次に見ていきます.
関数の最後にemployeeidに1追加して,返り値として返して終了です.



それでは次に社員情報を入れるクラスの中身を見てみます.

#社員情報クラス
class employee:

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

employeeという名前のクラスです[1].持っている値は,id,name,bd,salaryの4個です.それぞれ,社員ID,名前,生年月日,給与を表しています.この中で生年月日だけは年,月,日という3項目を分けて保持したほうが都合が良いため,別途クラスを定義しています.次はそれを見ていきます.

#生年月日クラス
class p_bd:

    def __init__(self,year,month,date):
        self.year = year
        self.month = month
        self.date = date

生年月日クラスは,年,月,日という変数を持っています.こうしておくことで表示する際にyyyy/mm/dd形式で表示するかyyyy年mm月dd日形式で表示するか選ぶのが容易になったり,あとから検索する際に月だけで検索するといったことが容易になります.



クラスの中身を見たところで,動作を表す関数に戻りましょう.次は社員情報の照会です.

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

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

ここは単純にprintでIDや名前といった列名を表示し,その下にfor文を使用してリストの最初から最後までを順に表示しています.
まずstringという変数に社員1人分のID,名前,生年月日,給与を間に空白を挟みながら代入しています.
次にprintでそのstringの中身を表示して次のループに進むという繰り返しです.
idは整数型なので,printで表示するために文字列に変換する必要があります.これはstr()関数を使用することで実行できます.
また,クラスの中の要素を取り出すときは,インスタンス名.要素名を使用するため,ここではi.idのように表記しています.
誕生日に関してはクラスの中にクラスが入っているため,i.bd.monthのように連ねて書くことで取り出しています.
給与は3桁区切りで表記する必要があるので,format関数を使用しました[6]



次は削除の関数の中身を見ていきます.

#削除
def delete(requiredid):

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

後述しますが,削除関数は編集関数の中でも呼ばれるので,mainから呼ばれたのか編集の関数から呼ばれたのかを判断する必要があります.そこで,引数としてrequiredidというものを与えることにしました.これが0であればmainから呼ばれた処理,すなわち単純に社員情報を削除する処理で,それ以外の数字であれば指定したIDの社員情報を編集するための削除であるという判断をします.
上記はrequiredidが0の場合なので,単純な削除の処理の場合の処理です.
削除したい社員のIDを入力させて,それはdeleteidに代入しています.



次にrequiredidが0以外の時の処理を見てみます.

    else:
        deleteid = requiredid

編集したい社員のIDをdeleteidに代入しています.後でも述べますが,編集は指定した社員の情報を削除して,上書きするという方法を取っているため,ここで指定した番号を1回削除する必要があります.



次は実際に削除する処理を見てみます.

    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の社員情報がありませんでした"

リストの中身を1個ずつ見ていき,その社員IDがdeleteidと一致するかどうかを判断していきます.
もし一致しなければ何もせずに次のループに進み,一致すればその社員情報を削除します.
requiredidが0であればmainから呼ばれた純粋な削除なので,ユーザに削除したことをprintで伝えます.
requiredidが0以外であればこれは編集処理の一部ですので,削除したことを表示するとユーザを驚かせてしまうため,何も表示しません.
削除が完了したらbreakによりループを抜けます.



最後に編集の処理を見てみます.

#編集
def edit():

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


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

ここはこれまでに記述した処理の組み合わせで実現しています.
最初にユーザから編集したい社員のIDを入力してもらい,それをdelete関数とregister関数に渡して処理を行っています.



細かいところでユーザの入力によって予期せぬことが起こったりするのですが,おおまかな流れはできました.次は私の知人のエンジニアにアドバイスしてもらって修正したものについて解説していきます.



参考文献
[1]Python-izm(http://d.hatena.ne.jp/alicehimmel/20101122/1290398381http://www.python-izm.com/)
[2]Python プログラミング 講座「ファイル入出力: 標準入出力、ファイルオブジェクト」(http://bacspot.dip.jp/virtual_link/www/si.musashi-tech.ac.jp/www/Python_IntroProgramming/02/index-2a.html)
[3]GeSource「文字列を分割する」(http://www.gesource.jp/programming/python/code/0016.html)
[4]Python式日本語版「2.組み込み関数」(http://python.g.hatena.ne.jp/muscovyduck/20080816/p1http://docs.python.jp/3.3/library/functions.html#int)
[5]主にプログラムを勉強するブログ「isdigit()について」(http://d.hatena.ne.jp/artgear/20120217/1329493335)
[6]こしごぇ(B)「Python の書式指定など」(http://koshigoeb.hateblo.jp/entry/2013/08/24/160215)