【Python 実践】現役 AI エンジニアが教える読み手に優しいソースコードの書き方

中別府大地

思いやりのあるソースコードを。

こんにちは、キカガクの中別府です。
普段は AI エンジニアとして、業務委託開発案件やコンサルティング事業を担当しています。
また、東北大学大学院医学系研究科非常勤講師として AI に関する学習コンテンツの提供、高知大学特任講師として、DX / AI に関する授業の作成・実施、AI 研究に関するゼミの運営を行っています。

本稿では、Python 初学者へ向けて、今後のために読み手を意識したソースコードの書き方を紹介します。

ソースコードは読む(読まれる)ことの方が多い

学び始めのうちはピンと来ないかもしれませんが、エラーを解決するためにドキュメントを参照したり、実装例を参考にするために GitHub を参照するなどソースコードを読む機会は意外と多いのではないでしょうか?

私の体感ではソースコードを書く・読むの割合は「書く:読む = 40%:60%」です。

この割合は立場によって変わりますが、プロジェクトに関わる人数が増えるほど読む(読まれる)割合が高くなると思います。

実際のプロジェクトの場合、自分にしかわからないコードは、読み手に伝わりづらく、追加の質問・説明が必要になるなど無駄なコミュニケーションコストを生むことになります。

これから本稿でお伝えする内容が参考になれば幸いです。

読み手に優しいソースコードとは

結論:可読性が高いこと

読み手が理解するのにかかる時間をどれだけ短縮できるかがポイントです。

PEP 8 – Style Guide for Python Code

いきなりですが PEP 8 をご存知でしょうか?
Python コードのコーディング規約がまとめられたものです。

非常に多くの規約が記載されていますが、この中ですぐに実践できるものを紹介します。

ソースコードの可読性を高めるテクニック

ここから 4 つのテクニックを紹介します。

  1. インデント・空白文字
  2. 命名規約
  3. docstring
  4. typing(型ヒントのサポート)

1. インデント・空白文字の扱い

インデントは 1 レベルでスペース 4 つを推奨します。

インデントは 1 レベルでスペース 4 つ
odd_numbers, even_numbers = [], []
for i in range(10):
    if i % 2 == 0: # 1 レベル
        even_numbers.append(i) # 2 レベル
    else:
        odd_numbers.append(i)

空白文字(半角スペース)は多用しすぎず適度に入れるのがポイントです。

空白文字を入れるポイント
# 非推奨
i=i+1
submitted +=1
x = x * 2 - 1
hypot2 = x * x + y * y
c = (a + b) * (a - b)

# 推奨
i = i + 1
submitted += 1
x = x*2 - 1
hypot2 = x*x + y*y
c = (a+b) * (a-b)

空白文字を入れるかどうかの判断は難しいところではありますが、( ) 内では使用しないことを意識するだけでもスッキリした見た目になります。

2. 命名規約

変数、関数、クラスは、それぞれ名前が決まっているもの、自身で名前を決めて定義するものがあります。
特に変数はローカル変数グローバル変数に分けられます。

  • ローカル変数:使用できる範囲が限られているもの(関数内で定義している変数など)
  • グローバル変数:どこからでも使用できるもの(どこからでも上書き・変更等が可能な変数)

ローカル変数に関しては関数やクラスが正しく動作するのであれば、あまり気にする必要は無いかもしれませんが、グローバル変数の名前は意識するようにしましょう。

用途命名規約
クラスCapWords 方式LinearRegression()
関数・メソッド小文字のみ使用し、必要に応じてアンダースコアで単語を区切るfit(), get_params()
定数大文字のみ使用しアンダースコアで単語を区切る(通常、関数の外側に書く)FILE_PATH
変数・引数小文字のみ使用し、必要に応じてアンダースコアで単語を区切るdf, df_train, df_test

CapWords 方式:先頭のみ大文字の単語をアンダースコアを使用せず繋げる

名前の付け方も意識しましょう。

関数の場合
# 非推奨:自分にしかわからない名前
def maeshori_1():
def maeshori_2():

# 推奨:名前だけで処理がイメージできる
def create_onehot_encoding():
def create_label_encoding():

3. docstring

docstring とはパッケージ、モジュール、関数(メソッド)の説明が記述されたものです。
例えば以下のようなもので help() で確認できます。

numpy.mean
import numpy as np
help(np.mean)

>>>
Help on function mean in module numpy:

mean(a, axis=None, dtype=None, out=None, keepdims=, *, where=)
    Compute the arithmetic mean along the specified axis.
    
    Returns the average of the array elements.  The average is taken over
    the flattened array by default, otherwise over the specified axis.
    `float64` intermediate and return values are used for integer inputs.
    
    Parameters
    ----------
    a : array_like
        Array containing numbers whose mean is desired. If `a` is not an
        array, a conversion is attempted.
    axis : None or int or tuple of ints, optional
        Axis or axes along which the means are computed. The default is to
        compute the mean of the flattened array.
...

パッケージ、モジュール、関数(メソッド)の処理内容の説明が記述されたものです。

自作した関数やクラスにも追加することができ、記載方法はダブルクオーテーションで囲み、内部に説明を記述するのが一般的です。

docstring 記載方法
def sample():
    """
    ここに説明を記述
    """

例:ダミー変数化の前処理

ダミー変数化
def create_one_hot_encoding(input_df, use_cols):
    """One Hot Encoding(ダミー変数化)による特徴量エンジニアリング

    Parameters
    ----------
        input_df: pd.DataFrame
        use_cols: list(str: カラム名)

    Return
    ------
        out_df: pd.DataFrame
    """

    out_df = pd.DataFrame()

    for col in use_cols:
        vc = input_df[col].value_counts()
        cat = pd.Categorical(input_df[col], categories=vc.index)
        out_i = pd.get_dummies(cat)
        out_i.columns = out_i.columns.tolist()
        out_i = out_i.add_prefix(f'{col}=')
        out_df = pd.concat([out_df, out_i], axis=1)

    return out_df

このように記述しておくと他の誰かが help() で内容を確認することもできます。

help で確認
help(create_one_hot_encoding)

>>>
Help on function create_one_hot_encoding in module __main__:

create_one_hot_encoding(input_df, use_cols)
    One Hot Encoding(ダミー変数化)による特徴量エンジニアリング
    
    Parameters:
    -----------
        input_df: pd.DataFrame
        use_cols: list(str: カラム名)
    
    Return
    ------
        out_df: pd.DataFrame

4. typing : 型ヒントのサポート

docstring の他に型ヒント(型アノテーション)も有効です。
こちらを使用すると以下のことがわかります。

  • 引数
    • 何をどんなデータ型で渡せばよいか
    • 例:input_df: pd.DataFrame
    • 例:use_cols: list
  • 返り値
    • どんなデータ型が返されるか
    • 例:-> pd.DataFrame

例:ダミー変数化の前処理

typing の記述
def create_one_hot_encoding(input_df: pd.DataFrame,
                            use_cols: list)-> pd.DataFrame:

    # 割愛

    return out_df

help(create_one_hot_encoding)

>>>
Help on function create_one_hot_encoding in module __main__:

create_one_hot_encoding(input_df: pandas.core.frame.DataFrame, use_cols: list) -> pandas.core.frame.DataFrame

まとめ

  • 全体的なコードの見やすさとして以下 2 点
    • インデント・空白文字の扱い
    • 命名規約
  • ソースコードの内容を理解する時間を短縮し、コード作成者以外でも扱いやすいように以下 2 点
    • docstring
    • typing : 型ヒント

上記が意識されているソースコードはとても読みやすく、自身が振り返る際にもとても便利です。

本稿の内容が今後の参考になれば幸いです。

こちらの記事もオススメ

まずは無料で学びたい方・最速で学びたい方へ

まずは無料で学びたい方: Python&機械学習入門コースがおすすめ

Python&機械学習入門コース

AI・機械学習を学び始めるならまずはここから!経産省の Web サイトでも紹介されているわかりやすいと評判の Python&機械学習入門コースが無料で受けられます!
さらにステップアップした脱ブラックボックスコースや、IT パスポートをはじめとした資格取得を目指すコースもなんと無料です!

無料で学ぶ

最速で学びたい方:キカガクの長期コースがおすすめ

一生学び放題

続々と転職・キャリアアップに成功中!受講生ファーストのサポートが人気のポイントです!

AI・機械学習・データサイエンスといえばキカガク!
非常に需要が高まっている最先端スキルを「今のうちに」習得しませんか?

無料説明会を週 2 開催しています。毎月受講生の定員がございますので確認はお早めに!

説明会ではこんなことをお話します!
  • 国も企業も育成に力を入れている先端 IT 人材とは
  • キカガクの研修実績
  • 長期コースでの学び方、できるようになること
  • 料金・給付金について
  • 質疑応答