AKAGI Rails

鉄道模型シミュレーターで遊んでいたはずが、気づいたらPythonなども。

Pythonらしいコードとは

Pythonらしさ」とは何か,という問に,簡単な答えを一つ用意するとしたら,「Python言語の利点を最大限活かした実装のやり方やコーディングスタイル」とでも言えるのではないでしょうか。本日は,「Pythonの禅」とPython標準のコーディング規約を紹介します。どちらも,Python公式から出されている PEP (Python Enhancement Proposals) という文書の一部であり,世界中のPythonプログラマに共有されているものです。

[PEP 20] The Zen of Python (Pythonの禅) を読もう

PEP 20 The Zen of Pythonでは,Pythonのデザインに関する原則について19行が記されています。日本語訳も多数見つかりますが,以下の記事が読みやすいでしょう。

qiita.com

The Zen of Python は哲学的な指針が列挙されているばかりで,すぐに実践するのは難しいかもしれませんが,どのような実装をすべきか悩んだときにはここに立ち返るとヒントが得られるかもしれません。

[PEP 8] Pythonコードのスタイルガイドを読もう

PEP 8では,Python標準ライブラリのPythonコードが従うべきコーディング規約が示されています。ただし,拘束力はそこまで強くないので,パッと見ですがPEP 8どおりでないPython標準ライブラリも無いわけではないみたいです。しかし,Python標準ライブラリのみならず,世界中のPythonコードに対してデファクトスタンダードのコーディング規約となっているフシがありますので,従っておいて損はありません。

PEP 8の日本語版は以下で読むことができます。

pep8-ja.readthedocs.io

すぐにでも実践したいいくつかの事項は,ここで改めて引用しながら紹介したいと思います。

インデント

Pythonコード内でのインデントには,半角スペース4つを使います。タブは原則として一切使いません。

関数などの途中で改行する場合の指針も示されていますが,厳密に唯一のルールが決められているわけではないので,ここは読みやすければある程度適当でもいいと思っています。

空行の入れ方

トップレベルの関数やクラスは、2行ずつ空けて定義するようにしてください。

クラス内部では、1行ずつ空けてメソッドを定義してください。

関連する関数のグループを分けるために、2行以上空けても構いません(ただし控えめに)。 関連するワンライナーの場合は、空行を省略しても問題ありません。(例: ダミー実装)

関数の中では、ロジックの境目を示すために、空行を控えめに使うようにします。

あまり空行を連発するとPythonコードとして品がないような印象を持たれます。

import

import文は原則,1モジュール1行ずつ記述します。

import文 は常にファイルの先頭、つまり モジュールコメントや docstring の直後、そしてモジュールのグローバル変数や定数定義の前に置くようにします。

import文 は次の順番でグループ化すべきです:

  1. 標準ライブラリ
  2. サードパーティに関連するもの
  3. ローカルな アプリケーション/ライブラリ に特有のもの

上のグループそれぞれの間には、1行空白を置くべきです。

PEP 8にはこのように書いてあるので,例えばVRMNXで使うならこのようになるはずです。

# LAYOUT

import os   # 標準ライブラリ
import sys

import vrmapi # ローカルのアプリケーションに特有のもの

# この行はatenxaのimportの前にどうしても必要なのでここに来る
sys.path.insert(0, vrmapi.SYSTEM().GetLayoutDir())

import atenxa  # ローカルのライブラリに特有のもの

# グローバル変数,定数定義
somedict = {}
NXSYS = vrmapi.SYSTEM()

def vrmevent(obj, ev, param):
    # 以下省略
    pass

コメントの書き方

PEP 8 -jaの本文に色々書いてありますが,一例を示せばちょうどこの上に書いた例のようになるはずです。インラインコメントでは,「スペース2つ以上 + # + スペース1つ」とすることになっていますが,スペースの使い方を忘れている方が多いです。

ドキュメンテーション文字列

別名 "docstrings" と言われています。別途PEP 257に規定があるので詳しく後述します。

命名規約

これは結構重要だと思っています。が,少なくともVRMNX界隈では今のところ皆さん好き勝手に書いているように見受けられます。

いちばん重要な原則

公開されているAPIの一部としてユーザーに見える名前は,実装よりも使い方を反映した名前にすべきです。

「撮る夫くん」の activate 関数を例に挙げてみます。 activate の実装は他ならぬイベントハンドラの実体にすぎませんが,撮る夫くんユーザーにはむしろ「撮る夫くんを有効化してくれる一行」として認識して頂くほうが理解しやすいだろうと考えられますので,toruo.vrmevent とか toruo.eventhandler とせずに,toruo.activate としました。

守るべき命名規約

簡単にまとめると以下のようになります。(標準ライブラリや組み込み関数,組み込み型は古い経緯があるのでこれに従わないケースがありますが,現在以降はこれに従うほうがよいでしょう。)

  • パッケージ・モジュールの名前は すべて小文字の短い名前にする。モジュール名では,必要であれば,アンダースコア _ は使ってよいが,パッケージ名では不可。

  • クラスの名前は原則, CapWords 方式とする。

  • 関数と変数の名前は,小文字のみ。読みやすくするために必要であれば,英単語をアンダースコア _ で区切る。

  • 定数扱いの変数は大文字のみ。必要に応じてアンダースコアで区切る。

  • 非公開プロパティ(モジュールの内部でのみ使用する前提であり,外部に公開していないグローバル変数や関数など)は,先頭をアンダースコア _ で始める

PEP 8では,関数の名前はクラスメソッドも含み,全部小文字ということになっていますが,肝心のvrmapiがCapWords方式で実装されているので困り果てています。ですが,各部品のイベントハンドラ(関数)は vrmevent とか vrmevent_xxx のように全部小文字+アンダースコアで生成されるので,大本営はvrmapiの中と外で命名規則を使い分けることを意図しているのでしょう。(本当に?)

VRM4~VRM5時代のVRMスクリプトはghost氏の提唱でCapWordsのハンガリアン記法が推奨されていましたが最早古いので,私の書くコードではあくまでPEP 8側に従っています。Pythonでは特に,クラスと関数がひと目見て区別できるコーディングスタイルは重要だと思います。

しかし,PEP 8の冒頭に「一貫性にこだわりすぎるのは、狭い心の現れである」とあるので,必要以上にとやかくは言いません。

比較に関する注意

None との比較には == は使いません。is または is not を使います。

# 正しい:
if foo is None:
    pass

if foo is not None:
    pass
# 間違い:
if foo != None:
    pass

if foo == None:
    pass

if not foo is None:
    pass

docstringsを書こう

コメントを書き込んでプログラムを分かりやすくしておくことは最早当たり前になっていますが(かといって徹底できていないのでおま言うw),関数の使い方やクラスの役割などを説明するコメントの書き方はある一定のルールが設けてあります。このルールに従っておくと,例えばVS Codeのような高機能エディタがうまく気を利かせて,コーディング中にポップアップでその説明を見せてくれたり,Sphinxのように自動でオンラインドキュメント化してくれるツールが存在したりするので,実務的にも得することができます。

docstringsの基本事項はPEP 257に規定されていますが,もう少し細かく書式を規定したものに,numpyスタイルとgoogleスタイルの二大流派があります。縦方向に省スペースでも書けるという点で,私はgoogleスタイルが好みです。

googleスタイルのdocstringsの書き方は,以下の記事が読みやすいです。

qiita.com

atenxaや撮る夫くんのコードにもgoogleスタイルでdocstringを書いています。コードごとSphinxに読み込ませると,こうやってドキュメントがほぼ全自動で作ってもらえるので大変便利です。

よいPython実装を真似しよう

The Zen of Pythonには,

There should be one-- and preferably only one --obvious way to do it. 何かをするための明らかな方法(実装)が,一つは……あわよくば,ただ一つだけの方法があるはずだ。

とはありますが,実際問題,Pythonプログラムには,実装の上手い下手や,実行速度の早い遅いが出てくるものです。

できるだけ分かりやすく,パフォーマンスも悪くない,上手な実装は,教科書のような書籍もいくつかありますが,有名なライブラリのコードを読んで,参考にして勉強するのもおすすめです。

個人的なおすすめをいくつか紹介します。

  • 標準モジュールのdatetime
    • カレンダー上の日付等を扱うモジュールです。曜日やうるう年の計算など,内容が分かりやすく,実装がきれいなので勉強になります。
  • seaborn (matplotlib拡張のデータ可視化ライブラリ)
    • matplotlibを内部で使用してmatplotlibよりも気が利いてきれいなグラフをささっと書いてくれるライブラリです。matplotlibのマニュアルを脇に開いて,seabornの実装を読み解くとPythonプログラミングの勉強にはなります。ですが分量は多いしmatplotlib自体も結構難解なので,普通に難解ではあります。VRMNXでは使う機会はないでしょうが,Pythonでのグラフ描きは比較的ポピュラーな用途かと思いますので紹介してみました。中級以上の方向け。

github.com

github.com

おわりに

Pythonまわりで実践したいこと,あるいは皆さんに布教したいことを勝手につらつらと書いてみました。PEP 8のコーディング規約も,GoogleスタイルDocstringsも,デファクトスタンダードとしてかなり有効に機能していますので,ご存じなかった方はぜひ参考にしてみてください。

今見てみると撮る夫くんではPEP 8やGoogleスタイルdocstringsの決まりごとを守れていない点がちらほら見受けられはしたので,若干反省もしておるところです。