初めに
Python パッケージを作成する際,個人的に気を付けていることをまとめます.
以下,Python 3.5 以降の使用を前提とします.
確認事項
フォーマットを統一する
開発者によって文字コードや改行コードが異なると厄介なので,EditorConfig を利用して統一しましょう.
root = true
[*]
charset = utf-8
end_of_line = lf
trim_trailing_whitespace = true
insert_final_newline = true
[*.py]
indent_style = space
indent_size = 4
依存パッケージのバージョンを明記する
他の開発者が実行環境を再現できるように,setup.py
や requirements.txt
に依存パッケージのバージョンを明記しましょう.
[options]
install_requires = numpy>=1.11.0, scipy>=0.17.0
pipenv
を使うと尚良いです.
コーディング規約に準拠する
複数人が一貫したコードを記述できるように,コーディング規約に準拠しましょう.
PEP 8 -- Style Guide for Python Code | Python.org
Flake8 や Pylint を利用して違反しているかどうかを確認します.無視したい警告がある場合,setup.cfg
や .flake8
にその旨を記述して下さい.
[flake8]
ignore = E221, E241, E251, E402
max-line-length = 79
autopep8
や black
を使うと,コードを自動で整形してくれます.
エンコーディング宣言を記述しない
UTF-8 でコードを記述する場合,次のエンコーディング宣言は推奨されないため,削除しましょう.
# coding: utf-8
ファイルの先頭でインポートする
特に理由がなければ,ファイルの先頭でインポートしましょう.ただし,依存パッケージを制限したい場合,関数の内部でインポートすることがあります.以下をご参照下さい.
- python - Is it pythonic to import inside functions? - Stack Overflow
- Should import statements always be at the top of a module? - Stack Overflow
- In Python, what happens when you import inside of a function? - Stack Overflow
命名規約を遵守する
次のように命名しましょう.
種類 | 例 |
---|---|
パッケージ | my_favorite_package |
モジュール | my_favorite_module |
クラス | MyFavoriteClass |
メソッド | my_favorite_method |
関数 | my_favorite_function |
変数 | my_favorite_variable |
定数 | MY_FAVORITE_CONSTANT |
組み込み関数を関数名(変数名)に充てない
Python は,60 以上の組み込み関数を備えています.
例えば,dict = {'a': 1.0}
と書くと,組み込み関数 dict
が上書きされてしまうので注意しましょう.
マジックナンバーを定数として保持する
マジックナンバーに適切な名前を付けて保持することで,意味が与えられ,後々の修正も容易になります.
品詞に配慮する
クラス名や変数名は名詞,メソッド名や関数名は動詞というように,品詞に配慮しましょう.
循環的複雑度を抑制する
循環的複雑度は,コードの複雑度を測る指標で,値が大きいほどバグを含む可能性が高いことを示唆します. setup.cfg
や .flake8
で設定することで,循環的複雑度を計測することができます.
[flake8]
ignore = E221, E241, E251, E402
max-complexity = 10
max-line-length = 79
分岐を減らす,または,メソッド(関数)を適当な単位に分割して,循環的複雑度を 10 以下に抑制してください.10 以下が難しい場合,任意の値を定めて下さい.
入れ子を浅くする
入れ子を浅くすることで,可読性が高くなります.ただし,循環的複雑度は下がらないことに注意して下さい.
アクセサを実装しない
Python は,属性を隠蔽しないため,直接代入,参照が可能です.したがって, 純粋なアクセサの実装は,無意味です.
ログを出力する
PEP 282 -- A Logging System | Python.org
ログを出力する場合,print
関数ではなく,logging.Logger
を利用しましょう.
処理したい例外を明示的に指定する
実行に失敗したとき,原因の特定が容易になります.
def div(a, b):
try:
ret = a / b
except Exception as e:
ret = np.nan
return ret
def div(a, b):
try:
ret = a / b
except ZeroDivisionError as e:
ret = np.nan
return ret
docstring を記述する
PEP 257 -- Docstring Conventions | Python.org
多くのパッケージは,次の記法を採用しています.
def div(a, b):
"""Division.
Parameters
----------
a
Dividend.
b
Divisor.
Returns
-------
ret
Result.
"""
try:
ret = a / b
except ZeroDivisionError as e:
ret = np.nan
return ret
PEP257 に準拠していることを pydocstyle
で確認して下さい.
doctest を記述する
全てのパブリッククラスや関数に doctest を記述してください.
def div(a, b):
"""Division.
Parameters
----------
a
Dividend.
b
Divisor.
Returns
-------
ret
Result.
Examples
--------
>>> a = 8.0
>>> b = 4.0
>>> div(a, b)
2.0
"""
try:
ret = a / b
except ZeroDivisionError as e:
ret = np.nan
return ret
型ヒントを記述する
PEP 484 -- Type Hints | Python.org
型に関する注釈を付けることで,可読性が高くなります.
def div(a: float, b: float) -> float:
"""Division.
Parameters
----------
a
Dividend.
b
Divisor.
Returns
-------
ret
Result.
Examples
--------
>>> a = 8.0
>>> b = 4.0
>>> div(a, b)
2.0
"""
try:
ret = a / b
except ZeroDivisionError as e:
ret = np.nan
return ret
PEP484 に準拠していることを mypy
で確認して下さい.
単体テストを実施する
開発途中であってもテストコードを必ず記述しましょう.
unittest
や pytest
,nose
を適宜利用して下さい.特に,pytest
を利用する場合,以下のような便利なプラグインがあります.
-
pytest-runner
: setuptools で pytest を実行 -
pytest-cov
: 単体テスト時に網羅率を計測 -
pytest-pydocstyle
: 単体テスト時にpydocstyle
を実行 -
pytest-flake8
: 単体テスト時にflake8
を実行 -
pytest-mypy
: 単体テスト時にmypy
を実行
単体テストは,CircleCI などの CI サービス上で実行し,coveralls や codecov に網羅率を記録できると望ましいです.
ドキュメントを作成する
Sphinx を用いてドキュメントを作成して下さい.
終わりに
CONTRIBUTING.md に以上のことを明記して開発の秩序を保ちましょう.
参考文献
Python のコーディング規約 PEP8 に準拠する - Qiita
循環的複雑度を下げたい - Qiita
ネストの深さは闇の深さ - Qiita
Pythonでロギングを学ぼう - Qiita
Python の logging について - Qiita
pythonのloggingモジュールでログ出力をする時に付加的な情報を出力する方法 - Qiita
Codecovを使ってカバレッジを計測する - Qiita
PythonプロジェクトのドキュメントをSphinxで作成する - Qiita