Pythonスキルの習得

Python初心者から中級者まで! コメントとDocstringの書き方をマスターしよう

Python初心者から中級者まで! コメントとDocstringの書き方を マスターしよう

こんにちは、Zero-Cheeseです。

コードを書く中で、

  • 頻繁に出現する「コメント」や「Docstring」の書き方について

解説します。

これらは、単なる説明文のように感じるかもしれませんが

  • コードの可読性を向上させ、
  • バグの発見・修正が迅速に行えるようになる効果があり、

ソフトウェアの品質向上に、つながります。

対象読者は、下記の通りです。

  • Pythonを学び始めた初心者
  • チーム開発効率を上げたい、エンジニア

前回の記事で、Sphinxを用いたドキュメンテーションの自動生成について、紹介しました。

今回は、ドキュメント自動生成に必須となる、Docstringの書き方を、ご紹介しています。

Sphinxを用いた、Pythonドキュメントの自動生成
Sphinxを用いた、Pythonドキュメントの自動生成Sphinxを用いて、Pythonプロジェクトのドキュメントを、効率的に生成するための方法を紹介します。Sphinxとその関連ツールを使用して、高品質なドキュメントを、簡単に作成する方法を解説します。...

コメント、Docstringの違い

コメントとは、

  • ソースコード内で、人がコードの理由や目的を、理解するための情報

実際のプログラムの動作には影響しません。

一方、Docstringは、

  • Pythonの関数やクラス、モジュールの先頭に記述され
  • ドキュメント化のために

使用されます。

Pythonコメントの書き方

単行コメントの書き方

Pythonでは#(ハッシュ)を使用して、単行コメントを記述します。

このコメントは、コード実行時に無視されます。

例:

# これは単行コメントの例です
print("Hello, World!")  # この部分もコメントです

複数行コメントの書き方

Pythonには複数行のコメントを、直接サポートする構文はありません。

しかし、三重クォート(''' または """)を使用して「文字列」として扱うことで、複数行コメントに、模倣することができます。

例:

'''
これは複数行のコメントの例です。
この部分は実際のコードとして実行されません。
'''
print("Hello, World!")

コメントのベストプラクティス

① コメントは、コードの直上または、右側(適切な場合)に配置する

関連するコードとコメントが離れていると、読者が混乱します。

② コメントを簡潔に保つ

コメントは簡潔かつ、明確にすることが重要です。

冗長なコメントは、コードの可読性を低下させます。

またコードが明確で、動作理由が明らかな場合、コメントは不要です。

③ コードの可読性を向上させる、コメントを心がける

下記を意識すれば、可読性が向上します。

  • コードの目的を、説明する
  • 複雑なロジックや、アルゴリズムを要約をする
  • 外部リソースや、参考文献へのリンクを明記する

ベストプラクティスに従ったコード、従わないコードの比較

ベストプラクティスに従ったコメント:
# 外部の数学モジュールを利用
import math

# 円の面積を計算
def circle_area(radius):
    return math.pi * radius ** 2  # 面積 = π * r^2

# 使用例
print(circle_area(10))

従わないコメント:
import math

# この関数は半径を引数にとって、それを使って円の面積を計算します。
# 円の面積の公式はπ*r^2です。この公式を使って面積を計算します。
# そしてその面積を、戻り値として返します。
def circle_area(radius):
    # ここで円の面積を計算します
    return math.pi * radius * radius  # ここでπ*r^2の公式を使って面積を計算しています

# ここで上記の関数を呼び出して、半径10の円の面積を計算しています
print(circle_area(10))

ベストプラクティスに従ったコメントは簡潔で、コードの機能や目的を、明確に説明しています。

一方、従わないコメントは冗長で、必要以上の情報を提供しており、可読性が低下しています。

Docstringの書き方

PythonのDocstringには、いくつかのスタイルがあります。

ここでは、よく使用される「3つのスタイル」を紹介します。

  1. SphinxスタイルのDocstring(正確には、reStructuredText(reST)形式)
  2. GoogleスタイルのDocstring
  3. NumpyスタイルのDocstring

Pythonの基本的なDocstringの書き方は、PEP257で示されています。

しかしPEP257では、特定のスタイルを示していないので、上記の3つのスタイルに従って、ドキュメントを記載するのが一般的です。

また本記事では、細かな点は省略している部分もあります。

上記のどのスタイルでも、ドキュメントの自動生成が可能です。

有名なドキュメント自動生成ツールとして、以下があります。

  • Sphinx
  • Doxygen
  • pydoc
  • Epydoc

① SphinxスタイルのDocstring

Sphinxスタイルという表現は存在しませんが、Sphinxの変換に適した形式という意味で、本記事では使っています。

正確には、reStructuredText(reST)というマークアップ言語を使います。

「特殊なタグ」を利用して、セクションを明確にします。

Sphinxでは、NumPyスタイルやGoogleスタイルのDocstringsも解釈できるよう、拡張機能を追加することができます。

よく使われる「特殊なタグ」

「ここだけ見てもよく分からない」と感じるかもしれませんので、後で紹介する「具体的なコード例」と合わせて、参考にして下さい。

  • :param [name]: と :type [name]:
    • :param: 引数の説明をするタグ
    • :type: その引数の型を示すタグ
  • :returns: と :rtype:
    • :returns: 関数やメソッドの戻り値を示すタグ
    • :rtype: その戻り値の型を示すタグ
  • :ivar [name]: と :vartype [name]:
    • :ivar: クラスのインスタンス変数の説明をするタグ
    • :vartype: そのインスタンス変数の型を示すタグ
  • :raises [ExceptionName]:
    • 発生する可能性のある例外を、説明するタグ

関数のDocstring

関数のDocstringでは、

  • その関数の目的、
  • 受け取る引数の情報、
  • 返り値、
  • および例外

を記します。

例:

def add(a, b):
    """
    二つの数値を足し合わせる関数。

    :param a: 一つ目の実数
    :type a: int or float
    :param b: 二つ目の実数
    :type b: int or float
    :returns: aとbの合計
    :rtype: int or float
    :raises TypeError: 引数が実数でない場合に発生します
    """
    if not (isinstance(a, (int, float)) and isinstance(b, (int, float))):
        raise TypeError("Both arguments should be int or float")
    return a + b

クラスのDocstring

クラスのDocstringでは、

  • クラスの概要や
  • そのクラスが持っている属性
  • メソッド

を記します。

例:

class Car:
    """
    車を表すクラス。

    :ivar color: 車の色を示す
    :vartype color: str
    :ivar speed: 車の現在の速度
    :vartype speed: int or float
    :raises ValueError: 不正な速度値が設定された場合に発生します
    """
    def __init__(self, color):
        self.color = color
        self.speed = 0

    def accelerate(self, amount):
        """
        車を加速させるメソッド。
        
        :param amount: 加速度(km/hでの増加量)
        :type amount: int or float
        """
        self.speed += amount

モジュールのDocstring

モジュールのDocstringは、

  • モジュールの先頭に記載し、
  • そのモジュールの全体的な内容や目的

を記します。

例:

"""
車や車関連のユーティリティを提供するモジュール。
このモジュールには、Carクラスやその他の関連関数が含まれています。
"""

② GoogleスタイルのDocstring

Googleスタイルは、Pythonのドキュメンテーションにおいて、非常に人気があります。

概要、属性、引数、戻り値、例外などの情報を明確に分けることで、情報を簡潔かつ、明確に伝える事ができます。

関数のDocstring

def add(a, b):
    """二つの数値を足し合わせる関数。

    Args:
        a (int or float): 数値の一つ目。
        b (int or float): 数値の二つ目。

    Returns:
        int or float: aとbの合計。

    Raises:
        TypeError: 引数が数値でない場合に発生します。
    """
    if not (isinstance(a, (int, float)) and isinstance(b, (int, float))):
        raise TypeError("Both arguments should be int or float")
    return a + b

Docstringの中には、

  • Yields:、Examples:、Note: 等もありませすが、

わかりやすさ優先で、省略しました。

クラスのDocstring

class Car:
    """車を表すクラス。

    Attributes:
        color (str): 車の色。
        speed (int or float): 車の現在の速度。
    """

    def __init__(self, color):
        """
        Args:
            color (str): 車の初期色。
        """
        self.color = color
        self.speed = 0

    def accelerate(self, amount):
        """車を加速させるメソッド。

        Args:
            amount (int or float): 加速度(km/hでの増加量)。
        """
        self.speed += amount

モジュールのDocstring

"""車や車関連のユーティリティを提供するモジュール。

このモジュールには、Carクラスや車に関連する関数が含まれています。
"""

③ NumpyスタイルのDocstring

Numpyスタイルは、科学技術計算ライブラリ「Numpy」のドキュメントで採用されているスタイルです。

このスタイルは、Numpy・Scipyのライブラリで使用されていることから、その名前がつけられています。

セクションの区切りが明確で、情報が整然と整理されています。

関数のDocstring

def add(a, b):
    """
    二つの数値を足し合わせる関数。

    Parameters
    ----------
    a : int or float
        数値の一つ目。
    b : int or float
        数値の二つ目。

    Returns
    -------
    int or float
        aとbの合計。

    Raises
    ------
    TypeError
        引数が数値でない場合に発生します。
    """
    if not (isinstance(a, (int, float)) and isinstance(b, (int, float))):
        raise TypeError("Both arguments should be int or float")
    return a + b

クラスのDocstring

class Car:
    """
    車を表すクラス。

    Attributes
    ----------
    color : str
        車の色。
    speed : int or float
        車の現在の速度。

    Methods
    -------
    accelerate(amount)
        車を加速させる。
    """

    def __init__(self, color):
        """
        Parameters
        ----------
        color : str
            車の初期色。
        """
        self.color = color
        self.speed = 0

    def accelerate(self, amount):
        """
        車を加速させるメソッド。

        Parameters
        ----------
        amount : int or float
            加速度(km/hでの増加量)。
        """
        self.speed += amount

モジュールのDocstring

"""
車や車関連のユーティリティを提供するモジュール。

このモジュールには、Carクラスや車に関連する関数などが含まれています。
"""

Docstringのベストプラクティス

効果的なDocstringを書くための、ベストプラクティスは、以下の通りです。

  • 簡潔で明確に: 要点を簡潔に、明確に伝えます。
  • 一貫性を保つ: 同じスタイルで書くことで、可読性が向上します。
  • 要約を先頭に: 主要な目的や動作を、最初に記します。
  • 型情報の提供: 引数や返り値の型を、明示します。
  • 例外を明記: 発生する可能性のある例外を、記述します。
  • 定期的な更新: コードの変更に合わせて、Docstringも更新します。

ベストプラクティスに従ったコード、従わないコードの比較

ベストプラクティスに従った例:
def calculate_area(width: float, height: float) -> float:
    """
    長方形の面積を計算する。

    与えられた幅と高さから長方形の面積を計算します。

    Parameters
    ----------
    width : float
        長方形の幅。
    height : float
        長方形の高さ。

    Returns
    -------
    float
        長方形の面積。

    Raises
    ------
    ValueError
        幅や高さが0以下の場合に発生。
    """
    if width <= 0 or height <= 0:
        raise ValueError("幅と高さは正の数である必要があります。")
    return width * height

以下のベストプラクティスが、適用されています。

  • 簡潔で明確に: Docstringが、関数の目的・引数・返り値・例外などの要点を簡潔に、明確に伝えています。
  • 一貫性を保つ: Docstringが、特定のスタイル(この事例では、Numpyスタイル)で整理されています。
  • 要約を先頭に: 関数の主要な目的が、Docstringの最初に記述されています。
  • 型情報の提供: 引数と返り値の型が、Docstringに明示されています。
  • 例外を明記: 発生する可能性のある例外が、Docstringに記されています。
従わない例:
def area(w, h):
    """面積を計算"""
    if w <= 0 or h <= 0:
        print("幅と高さは正の値である必要があります。")
        return
    return w * h

以下の点で、ベストプラクティスに従っていません。

  • 簡潔で明確に: Docstringが不十分で、関数の動作、引数、返り値、例外などについての情報が、不足しています。
  • 一貫性を保つ: Docstringが、特定のスタイルに沿っていません。
  • 要約を先頭に: 関数の目的がDocstringの最初に記述されていますが、内容が不十分です。
  • 型情報の提供: 引数と返り値の型が、Docstringに明示されていません。
  • 例外を明記: 発生する可能性のある例外が、Docstringに明示されていません。また、関数が不適切な引数を受け取った場合、適切な例外を送出せず、単にメッセージを出力しています。

最後に

コードの可読性は、コードの品質に直結します。

Pythonコードの品質は、そのコードを書くプログラマーの技術力だけでなく、そのコードが他の人にとってどれだけ理解しやすいか、また、将来的にメンテナンスや拡張が容易であるかも含めて、評価されるべきだと考えています。

したがってコードを書く際には、適切なコメント・Docstringを付け加えることが、重要です。

本記事が、Pythonコードの可読性やメンテナンス性を高める、手助けとなることを願っています。

最後までお付き合いいただき、ありがとうございました。