Python学習チャンネル by PyQ

Pythonのオンライン学習プラットフォームPyQのオフィシャルブログです

エンジニアの「プロの所作」04.困ったときは対処法の意味を理解してからコードを書こう

f:id:kenken0326:20190719170047p:plain

連載:エンジニアの「プロの所作」とは?

自分はプログラミング学習に向いていないのではないか

プロのエンジニアとして働いている姿が想像できない

そんなあなたに、コードを書くスキルだけではなく、普段プロのエンジニアがどのように考えて行動しているかをお伝えする連載です。

PyQは、Python学習者のあなたが「プロとして活躍できる」ということをサービス全体の目標として、 問題の作成、システムの構築、ユーザーの質問への回答、サポートを行っています。

そのサービス運営のポリシーを、Python学習者であるあなたと共有することで、 Python学習の先に目指しているものの1つ、「プロのエンジニア」という姿が明確になり、 不安が少しでも拭えればという思いで、連載を開始しました。

エンジニア「プロの所作」は14回の連載予定で、本記事は第4回となります。

こう思った時は、こう考えてみよう

f:id:kenken0326:20190725180836p:plain

  • こう思ったときは: 「これをコピペをすれば解決する」
  • こう考えてみよう:「これを使うとこうなるから解決できる」

あなたはプログラミングで困ったとき、よくわからずに場当たり的な対応をしてしまったことはありませんか?

誰もがプログラムを書いていて、上手く実装できない処理や解決できないエラーに遭遇する場面があると思います。プロのエンジニアでもこのような状況に陥ることがよくあります。(そもそも公式ドキュメントやWeb上の情報などのヒントなしでプログラムを書ける人はそうそういません)

ところが、初心者エンジニアとプロのエンジニアは困ったときの対処の仕方に違いがあります。

今回は「プロの所作」を通して、エラーの対処法についてお伝えします。

プロのエンジニアの解決策

プログラミング業務で解決できない問題に直面したとき、取るべき手段は大きく分けて2つあります。

プログラミングで困った時にやってみましょう

  1. 公式ドキュメントや技術書、技術ブログを参照する
  2. チームの仲間や会社の同僚に質問する

調べ方の「プロの所作」は、連載第1回 で解説しているのでチェックしてみてください。

意味を考えながら修正しましょう

問題に対して場当たり的に対処するのではなく、「どこで起きているのか」「どこを直すべきなのか」を考えて対処するのが「プロの所作」です。

3ステップで問題に対処しましょう

  1. どこが上手く実装できないのか、どこでエラーが起きているのか
  2. どのように実装するか、エラーを直すか
  3. 実装する箇所は正しいか、エラー対応を入れる箇所は正しいか = 実装の仕方・直し方は正しいか

実装の仕方から直し方まで考えて対処することで、同じような場面に遭遇したときにも応用がきくようになります。 余裕があれば解決法の背後にある仕組みを調べて知識を深めるのもよいでしょう。

ただし、調べすぎると自身の知識で理解しきれない情報にたどり着いて深みにハマってしまうこともあるので気をつけましょう。

場当たり的な対応をしてしまうケース

簡単な関数の設計を例にして、場当たり的な対応をしてしまうケースを見てみましょう。

実装したいコード

あるエンジニアが消費税込み価格を求める関数calc_tax_include()を実装したとしましょう。

# tax_incl.py
def calc_tax_include(price):
    """ 
    消費税込みの価格を求める関数
    """
    return int(price * 1.08)

def main():
    num = input('税込み計算:')
    print(calc_tax_include(num))

if __name__ == "__main__":
    main()

発生したエラーとヒント

入力値のパターンを試していたところ、以下のエラーが発生しました。

$ python3 tax_include.py
税込み計算:100
Traceback (most recent call last):
  File "tax_include.py", line 11, in <module>
    main()
  File "tax_include.py", line 8, in main
    print(calc_tax_incl(num))
  File "tax_include.py", line 4, in calc_tax_include
    return int(price * 1.08)
TypeError: can't multiply sequence by non-int of type 'float'

エラーメッセージの内容を調べると「シーケンスはintでない値(float型)と乗算できない」ことが原因とわかりました。

解決法をチームの仲間に相談したところ、input()関数は入力した値をstr型として返すというヒントを得られました。

修正と新たなエラー

そして、以下のようにプログラムを修正してみました。

def calc_tax_include(price):
    """ 
    消費税込みの価格を求める関数
    """
    # 引数priceをint型に変換するように修正
    return int(int(price) * 1.08)
$ python3 tax_include.py
税込み計算:100
108

無事に動きました。「100」と入力したら「108」と出力され、関数が期待通りの結果を出しました。

ところが「aaa」と数値以外の文字列を入力すると別のエラーが出てしまいました。

$ python3 tax_include.py
税込み計算:aaa
Traceback (most recent call last):
  File "tax_include.py", line 11, in <module>
    main()
  File "tax_include.py", line 8, in main
    print(calc_tax_incl(num))
  File "tax_include.py", line 4, in calc_tax_include
    return int(int(price) * 1.08)
ValueError: invalid literal for int() with base 10: 'aaa'

エラーメッセージから「10進数が渡されるべきint()関数に不正な値が入っている」ことがわかりました。
str型の値が入ってきたため、int型への変換でエラーが起きたということです。

例外処理を入れてみた

ここで例外処理を入れることにしました。

def calc_tax_include(price):
    """  消費税込みの価格を求める関数
    """
    try:
        # int型への変換で例外を捕捉する
        return int(int(price) * 1.08)
    except:
        # エラーが出たらメッセージを表示して0を返す
        print('整数を入力してください')
        return 0
$ python3 tax_include.py
税込み計算:100
108
$ python3 tax_include.py
税込み計算:aaa
整数を入力してください
0

整数を入力した場合は税込み金額が、文字列の場合はメッセージと0が返ってきました。 確かにエラーは起きなくなりましたが、これで良いのでしょうか。

このプログラムの良くない点

calc_tax_include()という関数の役割は「消費税込みの価格を求める」のはずですが、現状のプログラムには良くない点があります。

良くないところを確認しましょう

  1. 数値を文字列として渡しても動作する
    1. この関数は、数値を受け取って、数値を返す関数で十分だろう
    2. 関数に渡す際に文字列から数値に変換するほうが良い
     
  2. 不正な値(文字列)が入力された場合に0を返す仕様は、関数に期待する役割とは違う
    1. 計算の結果0になったのか、エラーの結果0になったのか関数呼び出し側で判断できない

このようにあまり良くない設計の関数になってしまったのは、解決法の仕組みや直すべき箇所を意識せずにエラーだけを直してしまったことが原因です。

「プロの所作」に照らし合わせて振り返る

これまでにやってきた対応を振り返ってみましょう。今まででプロの所作のうち「どこでエラーが起きているか」を知ることはできていました。
調べたりチームの仲間に聞いたりすることで、入力値の処理でエラーが発生していることを見つけられています。また、対処法としてint型への変換例外処理を考えているので「どうやってエラーを直すか」という観点もひとまず大丈夫そうです。

しかし「エラーに対処する箇所を考える」という観点を見落としていました。この観点が無いと場当たり的な対応になってしまいます。次はプロの所作を踏まえた解決法を見ていきます。

プロの所作を踏まえてエラー解決

ここで今回のプロの所作をおさらいしましょう。

プロの所作をおさらいしましょう

  1. どこが上手く実装できないのか、どこでエラーが起きているのか
  2. どのように実装するか、エラーを直すか
  3. 実装する箇所は正しいか、エラー対応を入れる箇所は正しいか = 実装の仕方・直し方は正しいか

このプロの所作を踏まえて修正したのが以下のプログラムです。

def calc_tax_include(price):
    """  消費税込みの価格を求める関数
    """
    return int(price * 1.08)

def main(): while true: # 数値が入力されるまでループする num = input("税込み計算:") try: # 入力された値をint型に変換する num = int(num) # エラーが出なければwhileループを抜ける break except ValueError: # エラーを捕捉してメッセージを表示する print("整数を入力してください") # int型に変換された値をcalc_tax_include()関数に渡す print(calc_tax_include(num))

__name__ == <span class="synConstant">&quot;__main__&quot;</span>: main()

どこでエラーが起きているのか

calc_tax_includeで最初にエラーが出たときを思い出してみましょう。 エラーになったのはこの2点でした。

エラーになったところの確認

  1. 数値を入力するとエラーになる
  2. 入力値をint型に変換し、文字列が入力されたときの例外処理

どちらも入力値が関係していることがわかります。

どうやってエラーを直すか

エラーの修正方法は自分で調べて得た情報や、同僚のヒントを参考にしました。
今回のプログラムは「 input関数の結果は文字列になるので、intで数値にしてから処理する」とエラーを直せます。

関数名から想像できる以上の処理を書かない

ここが今回のポイントです。前述のエラーがあったときに、「エラーが発生した箇所だから」と考えて calc_tax_include関数内でエラー処理をしてはいけません。

まず、ユーザーからの入力値のチェックはどこで行うのが適切かを考えます。 calc_tax_include という関数名から想像できる「税込み金額を求める」という処理以上のことは書かないようにするほうが良いでしょう。プログラムの規模が大きくなると、他の人も作成した関数を使います。その際に、関数名から想像できる以上の処理が行われていると、バグの原因となる可能性があるからです。

今回の例で問題になっているのは入力値だけです。この事実に着目してmain()関数の中でユーザーからの入力を受け取っている箇所を中心にエラーの対処を追加することにしました。

まとめ

プログラムを書いていて処理の実装、エラーの解決に困った時には一度立ち止まってみましょう。手を動かして対処する前に、以下のことを考えるといいでしょう。

  1. 問題を解決する方法は間違っていないか
  2. 処理を追加する箇所や直す箇所はプログラムの意図に対して適切か

場当たり的な対処をすることを避けて、プログラムの意図に合った対処法を考えることによって1つの問題から得た経験を幅広く活かすことができるようになります。

Copyright ©2017-2019 BeProud Inc. All rights reserved.