Pythonを今まで適当に書いてきましたが、プログラムをしっかりと作りたいと考え、いろいろ勉強・調査しました。そこで学んだことをこの記事に書きます。
参考情報
- Google Python Style Guide: 全部読み切れていませんが、いいこと書いてあります。
- エキスパートPythonプログラミング改訂2版: 初中級というより中級者向け。読み飛ばしている部分も多いですが、懐に置いておいて必要時に読みたい本。一皮むけたい人にはとてもおすすめ。
コーディング規約
PythonにはPEP8というコーディング規約があります。せっかくあるので従うべきでしょう。PyCharmのようなIDEを使うと警告を出してくれるので効率よくPEP8に準拠したプログラムを書くことが出来ます。
私感で重要と考えるものだけ簡潔に抜粋しておきます。
オブジェクト | ルール | 例 |
---|---|---|
パッケージ(フォルダ名) | 全小文字の短い名称。アンダースコア非推奨 | sample |
モジュール | 全小文字の短い名称。読みやすいならアンダースコア可能 | sapmle_module |
クラス | パスカルケース | SampleClass |
例外 | パスカルケースで末尾は"Error" | SampleError |
関数・変数 | 全小文字でアンダースコア可能 | sample_function |
関数やメソッドに渡す引数 | インスタンスメソッドの第一引数名self クラスメソッドの第一引数名は cls 引数名が予約語と衝突していた場合、アンダースコアを末尾に追加 |
sample_parameter |
メソッド名とインスタンス変数 | 全小文字でアンダースコア可能 非公開のメソッドやインスタンス変数は接頭辞がアンダースコア |
sample_public _sample_private |
定数 | 全大文字でアンダースコア可能 | SAMPLE_CONSTANT |
命名規約(アンダーバーを変数前後につける場合)
変数の前後にアンダーバーをつける場合があり、その説明を紹介します。基本的にクラス内のPrivateな変数はアンダーバーを2つ接頭辞にするのがよさそうです。
_single_leading_underscore
: "内部でだけ使う" ことを示します。 たとえば from M import * は、アンダースコアで始まる名前のオブジェクトをimportしません。
single_trailing_underscore_
: Python のキーワードと衝突するのを避けるために使われる規約です。例を以下に挙げます:
__double_leading_underscore
: クラスの属性に名前を付けるときに、名前のマングリング機構を呼び出します (クラス Foobar の __boo という名前は _FooBar__boo になります。以下も参照してください)
__double_leading_and_trailing_underscore__
: ユーザーが制御する名前空間に存在する "マジック"オブジェクト または "マジック"属性です。 たとえば init, import, file が挙げられます。この手の名前を再発明するのはやめましょう。ドキュメントに書かれているものだけを使ってください。
プロジェクト構造
残念ながらプロジェクト構造についてPEP8に記載がないです。いくつか参考にした情報です。
シンプルな方式
ゼロから学ぶ Pythonから抜粋。
(project)
├── (project) ............ プログラムのソースコードディレクトリ
│ ├── __init__.py
│ └── *.py
└── tests ................ 単体テストのソースコードディレクトリ
├── __init__.py
└── *.py
機械学習系
データサイエンスプロジェクトのディレクトリ構成どうするか問題から抜粋。notebook以下がメインとなるプログラム?
.
├── README.md
├── config
│ └── example.ini
├── converter
│ ├── __init__.py
│ └── converter.py
├── data
├── experiment.py
├── notebook
│ └── sample.ipynb
├── requirements.txt
├── results
└── tool
└── tool.py
Google AI Platform式
Recommended project structureから抜粋。このくらいシンプルな方が好きなのですが、ローカルでも動かすとなるとデータ・ログはどこに置く?という感じ。
docstringを使う
docstringは関数やクラスなどへのソースコード上のコメントによる説明です。あとで自分で読み返した時や、他人に共有するときなどやはり書いておくといいですよね。また、書いておくとツールを使ってドキュメントの自動生成もできるようです。
Pythonでは、主にGoogle StyleとNumpy Styleがあるようです。
「Google vs NumPy」を見ると比較がわかりやすいのですが、Google Styleの方が行数を少なく書くことができます。行数が少ないソースが好きなので、私はGoogle Styleを使います。
PyCharmではPython 統合ツールから設定すると、Styleに応じた補完などをしてくれます。
docstringのセクション一覧
よく使うセクション一覧です。
セクション | 内容 |
---|---|
Args | 関数引数 |
Returns | 関数戻り値 |
Raises | 関数例外 |
Examples | 関数の使用例 |
Notes | 関数/クラスの注記 |
Attributes | クラス属性 |
docstringの例
###例1 関数
def fetch_bigtable_rows(big_table, keys):
"""関数に対する1行のサマリ説明
サマリ説明から1行を空けて詳しい説明を書きます
Retrieves rows pertaining to the given keys from the Table instance
represented by big_table. Silly things may happen if
other_silly_variable is not None.
Args:
big_table: 引数1の説明
keys: 引数2の説明
Returns:
戻り値の説明。複数行に渡って書くのもOK。ここでの例は戻り値の具体例まで出していてわかりやすい。
example:
{'Serak': ('Rigel VII', 'Preparer'),
'Zim': ('Irk', 'Invader'),
'Lrrr': ('Omicron Persei 8', 'Emperor')}
If a key from the keys argument is missing from the dictionary,
then that row was not found in the table.
Raises:
IOError: 例外の説明
Example:
result = fetch_bigtable_rows(big_table, keys)
"""
例2 クラス
class SampleClass(object):
"""関数に対する1行のサマリ説明
サマリ説明から1行を空けて詳しい説明を書きます
Longer class information....
Attributes:
likes_spam: 属性1の説明
eggs: 属性2の説明
"""
def __init__(self, likes_spam=False):
"""Inits SampleClass with blah."""
self.likes_spam = likes_spam
self.eggs = 0
def public_method(self):
"""Performs operation blah."""
型ヒント(Type Hints)を使う
型ヒント(Type Hints)を使ってアノテーションして関数の引数・戻り値に対して型を明示することができます。Pythonは動的型付け言語なので型指定をしないでいいという反面、関数の引数・戻り値がわかりにくいです。型ヒント(Type Hints)を使えば、PyCharmなどのIDEで色々とサポート(代入する値が違っているのではとの警告など)をしてくれたりします。
書くのが面倒な反面、可読性が上がるので基本的には型ヒントを使うのが望ましいかと思います。
型ヒントはPEP484とPEP526で規定されていて、typingパッケージがサポートします。「typing モジュールは暫定的に標準ライブラリに追加されました
」と書かれていてまだ「暫定」扱いだそうです。
型ヒントの基本
引数はコロンの後にスペースをはさんで型を書きます。
戻り値は->
の後に型を書きます。
def greeting(name: str) -> str:
return 'Hello ' + name
以下が基本的な型です。
- str
- int
- float
- bool
- None
型ヒント(辞書型、タプル型、リスト型)
よく使う辞書型、タプル型、リスト型です。
typingパッケージからインポートして使います。
returnが複数の場合は、Tuple
を使います。
from typing import Dict, Tuple, List
def test(dict_: Dict[str, int], list_: List[str]) -> Tuple[str, int]:
print(dict_)
print(list_)
return 'aa', 1
型ヒント(オブジェクト)
オブジェクト名を書きます。オブジェクト名を調べるのが少し面倒です。
from logging import getLogger, Logger
def get_module_logger() -> Logger:
logger = getLogger(__name__)
return logger
この辺を参考にしました。とてもわかりやすかったです。当記事では、基本だけ書いたのですがもっと複雑なこともできます。
GitHubを使う
開発・検証したプログラムをすべてローカルに置いておくのは面倒です。GitHubは情報共有もできて、やはり非常に便利。共同開発をいない場合にも使わない手はないです。
記事「Git初心者がプログラムをGitHubに置いておく方法」のレベルで使っています。
Pythonに限定するなら、Python用".gitignore"テンプレートを使うと、少しリポジトリがすっきりします。
ログ出力
いつまでもprint
だけに頼っていられません。
"logging"パッケージを使うことで、ファイルにログを簡単に残しておくことができたり、出力元ソースコードや日時を自動出力できたりとメリット多いです。
詳しくは、記事「Pythonでprintを卒業してログ出力をいい感じにする」を見てください。