こんにちは、Zero-Cheeseです。
コードを書く中で、
- 頻繁に出現する「コメント」や「Docstring」の書き方について
解説します。
これらは、単なる説明文のように感じるかもしれませんが
- コードの可読性を向上させ、
- バグの発見・修正が迅速に行えるようになる効果があり、
ソフトウェアの品質向上に、つながります。
対象読者は、下記の通りです。
- Pythonを学び始めた初心者
- チーム開発効率を上げたい、エンジニア
前回の記事で、Sphinxを用いたドキュメンテーションの自動生成について、紹介しました。
今回は、ドキュメント自動生成に必須となる、Docstringの書き方を、ご紹介しています。
コメント、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つのスタイル」を紹介します。
- SphinxスタイルのDocstring(正確には、reStructuredText(reST)形式)
- GoogleスタイルのDocstring
- 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コードの可読性やメンテナンス性を高める、手助けとなることを願っています。
最後までお付き合いいただき、ありがとうございました。