連載:エンジニアの「プロの所作」とは?
「自分はプログラミング学習に向いていないのではないか」
「プロのエンジニアとして働いている姿が想像できない」
そんなあなたに、ただコードを書くスキルだけではなく、普段プロのエンジニアがどのように考えて行動しているかをお伝えする連載です。
PyQは、Python学習者のあなたが「プロとして活躍できる」ということをサービス全体の目標として、 問題の作成、システムの構築、ユーザーの質問への回答、サポートを行っています。
そのサービス運営のポリシーを、Python学習者であるあなたと共有することで、 Python学習の先に目指しているものの一つ、「プロのエンジニア」という姿が明確になり、 不安が少しでも拭えればという思いで、連載を開始しました。
エンジニア「プロの所作」は14回の連載予定で、本記事は第2回です。 第2回は、前後編に渡ってお伝えし、本記事は後編となります。
- 連載:エンジニアの「プロの所作」とは?
- 自分主体で考えて作る - エラーや問題の本質をまず知る
- エラーは出ないが思った通りに動かない時
- 関数やクラスを書く場合の、「思った通りに動かない」の解決方法
- 仕様に近づけていく実装の具体例:スポーツクラブでお客様に適切な会員種別をおすすめするプログラム
- まとめ
自分主体で考えて作る - エラーや問題の本質をまず知る
PyQは、「自分主体で考えて作る」を実践するのがプロのエンジニアと考えています。
エラーや思ったとおりに動かない現象が出たときにまずは本質(原因)を知ることが大事です。
前編では「エラーが出る場合」の所作について説明しました。
後編は「思ったとおりに動かない場合」の所作についてです。
エラーは出ないが思った通りに動かない時
プログラムにはエラーにはならないけど、思ったとおりに動かない場合があります。
思ったとおりに動かない場合、2種類の可能性があります。
実装が悪い場合と、仕様が明確になっていない場合です。
「作りたい処理は何だろう?」 まずは自分がやりたいことを明確にする必要があります。作りたいものを深く理解せずに作り始めるとどうしたらいいのか迷子になる場合が多くあるからです。 まずはやりたいことが明確になっているか、以下の順に確認しましょう。
やりたいこと(仕様)を明確にする為の確認手順
- 作りたいものを書き出そう(書かれているものを読もう)
- 書き出された仕様が十分かチェックしよう
- 書き出された仕様に、明確になっていない条件や動作はあるか?
関数やクラスを書く場合の、「思った通りに動かない」の解決方法
作る前にまず何を作るのかをまとめる作業は、「画面」や「機能」を考える場合にも、関数やクラスを書く場合にも有効です。ここからは「関数やクラスを書く場合」を想定して、「思ったとおりに動かない」の解決方法を説明します。
仕様をまとめよう
どういう動作の関数やクラスになるかを明確にするために、書き出すことをオススメします。
Pythonの場合は docstring
という機能を使って、関数やクラスの仕様を書き出せます。
以下の2,3行目の文字が calc_tax_inc
の docstring
に書かれた仕様です。
def calc_tax_incl(price, tax=0.08): """ 引数 price の金額を、税込みの金額にして返す 引数 tax を指定すると税金の率を変更できる """ return int(price * (1 + 0.08))
内部の処理でなく関数の入力、出力の意味、「どう問題を解決するものか」を説明することが大切です。
仕様に実装を近づけていこう
作りたいものが明確になった後は、次はどうやって思い通りに動かない状態から仕様に近づけるかを考えます。
大まかに以下の順に考えると良いでしょう。
仕様に実装を近づける為の手順
- 作りたい処理が何かをまとめる
(頭の中でも良いですが、メモ帳にでも書くほうが安心です) - 関数やクラスを作る
- 関数の分割やクラスの分割をイメージできれば分割してください
- それぞれの関数やクラスに、docstringで仕様だけ書いておきましょう
- 関数の中の処理や条件分岐を実装する
- もし「この処理はまとめられるのでは」と思えば関数やクラスに分割する
- docstringに「この処理は何をするものか」の仕様を書いてください
- 仕様が明確に書けない場合は処理の分割方法が悪いです
- 内部の処理でなく、関数の入力、出力の意味を説明することが大切です
- 3,4のプロセスを繰り返す
関数やクラスに処理を分割する意義
関数やクラスに処理を分割するのは大切です。 大きな1つの処理を書こうとすると、変数などの状態が増えてプログラムが難しくなるからです。
また、関数を適切に分離することでテストも書きやすくなります。
データの変化などは、logging を使ったり、pdb やprintデバッグを使って確認できますが、この記事では詳しく説明しません。
仕様に近づけていく実装の具体例:スポーツクラブでお客様に適切な会員種別をおすすめするプログラム
具体的な例を紹介します。
某スポーツクラブでお客様に適切な会員種別をおすすめするプログラムを作りました。 お客様には利用時間(フルタイム、ディタイム、ナイト)、学生かどうか、年齢の3種類の情報を入力してもらい、一番価格が安くなる会員種別を返します。
会員種別
- フルタイム: 利用時間9:00〜22:00, 10000円
- ディタイム: 利用時間9:00〜18:00, 7000円
- ナイト: 利用時間17:00〜22:00 , 8500円
- 学生: 利用時間9:00〜22:00,6000円
- シルバー(65歳以上): 利用時間9:00〜22:00, 8000円
情報の入っている変数
use_time
: 定数FULL_TIME
,DAY_TIME
,NIGHT_TIME
の各値と比較するstudent
:True
か、False
age
: 年齢の数字が入っている
プログラムの問題点
以下のプログラムは、70歳で学生ではない、ディタイムだけ利用するお客様にシルバー会員をすすめます。 実際は、シルバー会員(8,000円)よりディタイム会員(7,000円)のほうが価格が安いので、ディタイム会員を返す必要があります。
現在のプログラムは、期待する結果を返さない状態です。
現在のプログラム
会員の判定部分は以下のように書かれています。これを正しい動きをするように修正しましょう。
# 学生かどうかで判定 if student: return '学生(6,000円)' # シルバーかどうかで判定 elif age >= 65: return 'シルバー(8,000円)' else: # 利用時間で判定 if use_time == DAY_TIME: return 'ディタイム(7,000円)' elif use_time == NIGHT_TIME: return 'ナイト(8,500円)' else: return 'フルタイム(10,000円)'
今回の問題点と解決の指針
今回の問題で考えられることは「一番価格が安くなる会員種別を返す」ことが考慮されていないことです。
確認したところ、テストケースに65歳以上のお客様がディタイム利用する場合が考慮されていませんでした。テストケースを追加し、実装を修正します。
仕様を読むと一見、学生かどうかシルバーか通常会員かどうかを選んでから、通常簡易位の場合は利用時間を選ぶ仕様に見えます。 そのため修正前のコードは、学生とシルバーかどうかを先に判定しています。
しかし、実は「一番価格が安くなる会員種別を返す」がポイントですので、if文の条件を安い順に判定すればよいのです。
実装する
まずは、料金が安い順に会員を並べます。
- 学生(6,000円)
- ディタイム(7,000円)
- シルバー(8,000円)
- ナイト(8,500円)
- フルタイム(10,000円)
これにしたがって、コードは以下のように修正しました。
# 学生かどうかで判定 if student: return '学生(6,000円)' # 利用時間がディタイムかどうかで判定 elif use_time == DAY_TIME: return 'ディタイム(7,000円)' # シルバーかどうかで判定 elif age >= 65: return 'シルバー(8,000円)' # 利用時間がナイトかどうかで判定 elif use_time == NIGHT_TIME: return 'ナイト(8,500円)' else: # どれにも当てはまらない場合 return 'フルタイム(10,000円)'
これで正しい結果を返すようになりました。
まとめ
エラーが出た場合と同じように 思ったとおりに動かない ときも原因を明確にするのが大事です。
解決できない場合は、「なんだか動かないんですが。」と報告するのではなく、分かる範囲で仕様を明確にし、期待する動作、現在の動作をまとめてから相談するようにしましょう!