step12 課題レポート:「電話帳データベース」【後編】

『山之口洋の極・楽 python 講座 【基礎編】』の卒業課題である「電話帳データベース」に、皆さんは鋭意取り組んでいると思う。このstepでは、いくつかのヒントと、これまでのstepでは扱い損ねた内容についても解説している。

ファイル形式を独自に定義するには

アプリ実行中、名前と電話番号のデータは、ディクショナリで保持されている。永続化のためには、データは、アプリ終了時には特定の形式でテキストファイルに保存し、アプリ起動時にはそのファイルから読み込まなくてはならない。
1人分のデータ(名前と電話番号のペア)は、ファイルの1行に対応させると便利だが、それには格納フォーマットを決めなければならない。簡単ながらデータ設計の作業で、たとえば、

といった細々したことをもれなく考えなければならない。
1行が1レコードからなり、1レコードがいくつかのフィールド(ここでは名前、電話番号など)からなるデータには、すでに以下の定型フォーマットがあるので、どちらかを使えばよい。キーとなる文字列(つまり名前)との関係しだいで、どちらにも一長一短がある。
TSV: tab separated value
TAB記号で区切られた値を意味する。

CSV: comma separated value
カンマで区切られた値を意味する。

これらの各行を読み込む時は、どのように処理すればよいのか。それには文字列メソッドsplitを使う。例を示す。


name, num = line.split(',')
PB[name] = num[:-1]

1行目では文字列変数lineに読み込んだ文字列を、カンマで分割して2つの文字列変数に格納している。2行目は、numの最後の文字(改行)を切り落として、ディクショナリに格納する処理である。
CSV形式は、この例のように空白を含む名前を扱えるが、カンマが含まれる名前はうまく処理できない。その場合は、名前全体を""で囲むのが一般的だが、その処理はやはり自分で書かなくてはならない。

pickleを使う

pythonに備わっているシリアライズ※1の仕組みpickleを使えば、データ設計の悩みから解放される。pickleとは、ほら、漬物のことだ。
pickleは、プログラム中のあらゆるデータを、1次元のバイト列(文字列ではない)に変換する。そしてpickleファイルという、拡張子が.pklのファイルに格納する。永続的データを実現する手軽な手法なので、ぜひマスターするべきである。
pickleを使うには、はじめにpickleモジュールをインポートする必要がある。


import pickle

1 直列化。オブジェクトのデータをバイト列に変換すること。その逆はデシリアライズ(非直列化)である。pythonではあらゆるデータがオブジェクトであり、数値、文字列、リスト、ディクショナリなどから、独自に作ったクラスオブジェクトまで、基本的にはすべてを永続的に格納できる(一部pickle化できないオブジェクトも存在する)。

たとえば電話帳がディクショナリphoneに格納されているとき、これをpickleにしてファイル保存するには、次の処理でよい。


#    電話帳をpickleファイルへ書き出し
with open('phone.pkl', 'wb') as f:
    pickle.dump(phone, f)

withは初出だが、使い終えた時にファイルを自動的にcloseしてくれる仕組みである。
一方、ファイルからオブジェクトにデータを読み込むには、以下のようにする。


#    電話帳をpickleから復元
phone = dict()
try:
    f = open('phone.pkl', 'rb')
    phone = pickle.load(f)
except:                # pickleファイルがなくても異常終了しない(初回起動対応)
    print('初回起動です')
else:
    f.close()

書き出しに比べやや複雑なのは、初回起動時には、まだpickleファイル(phone.pkl)が存在しないからである。

クォートと複数行文字列

「電話帳データベース」と直接関係ないが、ここまでのstepで十分に説明できなかったことの1つが文字列定数の書き方だ。
pythonでは文字列定数を、シングルクォート「'」またはダブルクォート「"」で囲む。なぜ2種類の引用符を認めるかと言えば、引用符を含む文字列をスマートに表現するためだ。たとえば、

のように、シングルクォートを含む文字列はダブルクォートで囲み、逆にダブルクォートを含む文字列はシングルクォートで囲むことで、他の言語のように美しくないエスケープ文字「\'」「\"」を使わずに「'」「"」自体を表現できる。これはなかなか考えられた、新しい解決法だ。
さらに「トリプルクォート(''')」もある。なにやら用語が混乱しているが、これは3つのシングルクォート(こちらを奨める)(''')または3つのダブルクォート(""")のことだ。以下のように、複数行の文字列を直接表現できる。Perl言語などのヒアドキュメントを洗練したものである。


    scenestr = input('''
    番号を入力してください。
    1:電話番号を検索
    2:電話番号を登録
    3:電話番号を削除
    4:電話番号を一覧
    0:プログラムの終了
    -> '''[1:])

この1文だけでメニュー画面を表示し、ユーザが入力した数字(文字列)を変数scenestrに代入する。2つのトリプルクォートの間は、改行文字も含む1つの文字列定数で、input関数でプロンプトとして指定される。スライス表現[1:]は、最初の改行文字を取り除く働きをする。
この後で、scenestrが数字であるか、正しいコマンドの範囲内であるかのチェックをすればよい。int()関数による整数化がそれに続くだろう。
トリプルクォートによる複数行文字列は、始めは戸惑うが、慣れると手放せなくなるほど便利だ。{ }とformatメソッドによる書式指定step08参照)と組み合わせれば、ほとんどワープロの差込み文書に近いことまで実現できる。

エラー処理とシステムテスト

ある学生の独白。
【基礎編】の講義が始まったばかりのころ、オレが作るサンプルプログラムはスッキリと単純だった。処理データもプログラム中に直接書き込まれていたし、テストといってもコマンド行を入れて「よっしゃ!」とばかり「Enter」キーをひっぱたけばそれでよかった。
だが、「ヘロンの公式」のプログラムを少しは実用的にしてやろうと、コマンドラインからデータを受け取りだしたあたりから、物事は複雑になりだした。簡単だった手順は正常系異常系の2つに分かれてからみ合い、なんだか意味不明なエラーメッセージや、いまだにうろ覚えのtry:~except:(~else:)構文が、スッキリしたプログラムを汚し始めた。それと同時に動作テストも1度じゃなく、何通りものテストセットで試せなどと、教師が面倒くさいことを言い出した。
そして最終課題に至って、ついにオレは気づいた。システムが大きくなればなるほど、エラー処理の種類も、完全なテストセットも、どんどん膨らんでくる、という恐ろしい事実に……。社会インフラだとかいう巨大システムの開発なんか、いったいどうなっちゃうんだろう……。

実は、その通りなのである。システムの規模が大きくなれば、やらなくてはならないエラー処理も急激に増え、すべてをあらかじめ網羅することが難しくなるし、プログラムも正常系と異常系が盛り蕎麦のように絡み合う。完全なテストセットなど用意できなくなる。恐ろしいことに、それらの複雑さや種類数は、システムの規模(行数)に比例ではなく指数関数的に増大するのだ。
そして、その事態を解決する魔法の杖は存在しない。

といった、世間で効くといわれているプロジェクト管理技法をチマチマと実行していく以外に、つまりずっとエラーやバグと闘い続ける以外に、方法はないのだ。

とはいえ、今回の課題レポートくらいならまだ安心しててよい。それに皆さんの多くがSEになって巨大プロジェクトに取り組むわけでもない。
言いたいのは、このくらいの段階から、エラー処理とシステムテスト、デバッグについて、しっかりした考え方を持っていかないと、ある規模以上のプログラムが書けない残念なプログラマで終わってしまうということなのだ。言語にもよるが、1人のプログラマーが管理できるプログラムの規模は10,000行くらいと言われている。自分の仕事用ツールや、趣味でゲームアート系プログラムを書くくらいなら、まあ1,000行で足りるが、それはすでにいい加減な書き方が致命傷になる規模なのである。いいかげんな開発姿勢は、楽しいプログラミングを苦しい作業に変える。それでは、せっかくここまで身につけたプログラミングの知識も宝の持ち腐れになってしまう。
そうした残念な結果に終わらないためにも、ここから先は、よいプログラミング習慣をつけることを肝に銘じてほしい。
たとえば、プログラムを作るとき、テキストファイルを作るか、紙を1枚用意して、気づいたことをメモするのは、なかなかよい習慣である。また、プログラムには可能な限りコメントを書こう。「他人に読ませるわけじゃなし」と思うかもしれないが、半年後の自分は、もう他人である。

『山之口洋の極・楽 python 講座 【基礎編】』を終えて

この最終課題をクリアしたら、いよいよ、与えられた要求仕様を満たすだけでなく、自分のやりたいこと(コンピュータにさせたいこと)を要求仕様に落とし、プログラムとして表現するステップに歩を進めよう。世界中の優秀で気前のよいプログラマたちが作ってくれたライブラリを駆使すれば、pythonは短いプログラムで驚くべきことができる実力を秘めている。
中級編『山之口洋の極・楽 python 講座 【応用編】』は、そんな世界への道案内ガイドブックとして書いた。