Python のコーディング規約 PEP8 に準拠する

この [Qiita の連載記事](http://qiita.com/ynakayama/items)ではデータ分析のための主要言語として Python を利用してきました。ところでみなさんは Python のコーディング規約 PEP8 をご存知でしょうか。

# ソースコードスタイルガイド PEP8

ソースコードは一般に「書かれる時間」よりも「読まれる時間」の方が長い、そのような事実に基づいて、「スタイルを統一し読みやすいコードを書こう」というアイデアのもとに作られたのがこのガイドです。

Style Guide for Python Code
http://legacy.python.org/dev/peps/pep-0008/

本家は当然ながら英語ですが有志の方が日本語に翻訳してくださっています。

PEP8 日本語訳
https://github.com/mumumu/pep8-ja

どちらにせよ Python を利用する方は必ず一読するべきかと思います。

## 自動的にソースコードをチェックする

さて、長大なガイドを頭の中に叩き込んで漏れなくチェックするのは大変な労力となります。人間ですからミスすることもあるでしょう。そこでガイドラインに準拠しているかどうかチェックするツールがあると便利です。

Python の世界には [pep8](https://pypi.python.org/pypi/pep8) というガイドラインと同じ名前のコマンドがあります。これを使うことでチェックができます。

### PEP8 チェックツールを使う

まずは次のように pip でインストールしておきます。

このとき、ついでに [pyflakes](https://pypi.python.org/pypi/pyflakes) も一緒に入れておくと便利です。 pyflakes とは何かというと Python の文法チェックをしてくれるツールです。これは明らかな文法の誤りを検出するだけでなく、不要な import 文など無駄なコードも検出してくれます。ぜひあわせて導入しましょう。

```zsh
pip install pep8
pip install pyflakes
```

なお [Django](http://www.djangoproject.jp/) フレームワークで Web アプリケーションを作っているかたは [pyflakes の代替として flake8 を導入する](http://naoina.plog.la/2013/05/09/233720000000)と幸福が実現します。

### Python の文法チェックをまとめておこなう

pep8 と pyflakes を導入したら次のようにシェルスクリプトを作成しパスの通ったディレクトリに置いておきましょう。ちなみに筆者はこれを pyck というコマンドにしています。

```bash
setup_environment() {
    # Python のインストールパスを指定する
    PYTHON_PATH=/opt/python/current
    IGNORE_ERRORS=E221,E701,E202
}

main() {
    setup_environment
    which $PYTHON_PATH/bin/pyflakes > /dev/null || exit 254
    which $PYTHON_PATH/bin/pep8 > /dev/null || exit 254
    $PYTHON_PATH/bin/pyflakes $*
    $PYTHON_PATH/bin/pep8 --ignore=$IGNORE_ERRORS --repeat $*
    exit 0
}

main $*
```

チェックしたいファイルに対し pyck your_file.py とすると文法チェックとガイドラインへの適合チェックがなされます。何も出力されない場合はエラー無しということになります。なお上のスクリプトでは pep8 や pyflakes 自体が無い場合は戻り値 254 で終了するようにしています。

また、ガイドラインに完璧に適合させようとするとプロジェクトによっては困ることがあるかと思います。そのようなときには上のように pep8 --ignore オプションで無視したいガイドラインの番号を指定することができます。

なお、どのエラー番号がどういった文法に対応しているかは [PEP8 のドキュメント](http://pep8.readthedocs.org/en/latest/intro.html)を参照してください。

### 実際にソースコードをチェックする

ではさっそく例としてシンタックスチェックをしてみましょう。今回、サンプルとして次のようなファイルを用意しました。

```py3
#!/usr/bin/env python
# -*- coding: utf-8 -*-

import sys, os

def list_files(path):
    for root, dirs, files in os.walk(path):
        for filename in files:
            fullname = os.path.join(root, filename)
            print( fullname )

def main(args):
    path = args[1]
    list_files(path)

if __name__ == '__main__':
    if len(sys.argv) > 1:
        main(sys.argv)
```

このファイル os_walk.py は引数に指定されたディレクトリの中にあるファイル一覧をリストアップし画面に出力する簡単なプログラムです。

プログラム自体は確かに正常に動作します。しかし、これに対しシンタックスチェックをおこなうと、次のようにガイドライン違反が見つかりました。

```zsh
$ pyck os_walk.py 
os_walk.py:4:11: E401 multiple imports on one line
os_walk.py:6:1: E302 expected 2 blank lines, found 1
os_walk.py:10:19: E201 whitespace after '('
os_walk.py:12:1: E302 expected 2 blank lines, found 1
os_walk.py:19:1: W391 blank line at end of file
```

このように、何も考えずにソースコードを記述するとすぐにガイドライン違反が出てしまうことになります。

## より高機能なチェックツールを利用する

pep8 によるメッセージはどこを修正すればいいのか慣れるまで少々見づらいでしょう。そこでより親切にメッセージを表示してくれる [py.test を使う](http://dev.ariel-networks.com/wp/archives/2491)とさらに幸福が実現します。

まずはいつも通り pip でインストールします。

```zsh
pip install pep8 pytest pytest-pep8
```

あとは pep8 コマンドの代わりに py.test --pep8 で目的のファイルをチェックすれば OK です。さっそく先ほどの os_walk.py をチェックしてみましょう。

![2.png](https://qiita-image-store.s3.amazonaws.com/0/38501/57ee2c25-35da-0236-7c52-e9976f5e05e6.png)

素の pep8 より詳しいメッセージが表示されました。これならソースコードのどの場所に問題がありどう修正したら良いのか一目瞭然です。

また、筆者は使っていないのですが [PyCharm](https://confluence.oceanobservatories.org/display/CIDev/PyCharm+-+PEP8+and+Pyflakes+Integration) というツールもあります。こちらを利用しても良いでしょう。

## 自動的にソースコードを修正する

さてソースコードガイドラインへの適合はツールでチェックできるようになりましたが、これをすべて手作業で修正するのは時として大変な労力になります。そこで自動修正ツールを利用します。 [autopep8](https://pypi.python.org/pypi/autopep8) はまさにこの自動修正ツールです。さっそく導入しましょう。

```zsh
pip install autopep8
```

先ほどのサンプルコードに対し autopep8 os_walk.py としてツールを使ってみます。すると以下のように修正後のソースコードが標準出力に吐き出されました。

```py3
#!/usr/bin/env python
# -*- coding: utf-8 -*-

import sys
import os


def list_files(path):
    for root, dirs, files in os.walk(path):
        for filename in files:
            fullname = os.path.join(root, filename)
            print(fullname)


def main(args):
    path = args[1]
    list_files(path)

if __name__ == '__main__':
    if len(sys.argv) > 1:
        main(sys.argv)
```

たしかに問題は解決されているようです。これを実際にファイルに書き込んで反映させるには autopep8 -i os_walk.py のように -i オプションを利用します。

カレントディレクトリ以下のソースコードを一気に修正するなら次のようにすればまとめて修正できます。

```zsh
pep8 . | cut -d: -f 1 | sort | uniq | xargs autopep8 -i
```

また autopep8 も pep8 と同様 --ignore オプションで無視したいエラー番号を指定することができます。

ただし、何も考えずに autopep8 -i するのはかなり乱暴なやり方なので diff を確認したり pyflakes で文法チェックしたり [nose](https://pypi.python.org/pypi/nose/) などでテストを実行するなどして品質を確保することも忘れないでください。

先ほどのシェルスクリプトを次のように改良すれば -i オプションを指定すると autopep8 で自動修正、オプション無しだと単なるチェックという風にすることができます。

```bash
run_check() {
    $PYTHON_PATH/bin/flake8 --ignore=$IGNORE_ERRORS $*
}

autofix() {
    shift && $PYTHON_PATH/bin/autopep8 --ignore=$IGNORE_ERRORS -v -i $*
}

main() {
    setup_environment
    which $PYTHON_PATH/bin/autopep8 > /dev/null || exit 254
    which $PYTHON_PATH/bin/flake8 > /dev/null || exit 254
    test "$1" = "-i" || run_check $*
    test "$1" = "-i" && autofix $*
}

main $*
```

## エディタからチェックをおこなう

さて、こうなるとふだん使っている Emacs からシンタックスチェックをしたくなると思います。そんなときにオススメなのが python-pep8.el という Emacs Lisp です。

python-pep8.el
https://gist.github.com/ieure/302847

実際に利用するときには pep8 のパスを指定してください。次のようにパスを書き換えます。

```scm
;; /opt/python/current を Python のインストールパスとする場合
(defcustom python-pep8-command "/opt/python/current/bin/pep8"
  "PEP8 command."
  :type '(file)
  :group 'python-pep8)
```

あとは python-pep8.el を Emacs のロードパスが通ったディレクトリに置いてロードすれば良いでしょう。次の Emacs Lisp は python-pep8 をロードし、シンタックスチェック用のキーバインドとして C-c p を割り当てます。

```el
(when (load "python-pep8")
  (define-key global-map "\C-c\ p" 'python-pep8))
```

これで Emacs で Python のコードを開いて C-c p すれば pep8 によるシンタックスチェックができます。

![3.png](https://qiita-image-store.s3.amazonaws.com/0/38501/16c7aaf5-c627-d60c-053b-7fc9c9543649.png)

シームレスにシンタックスチェックとその修正がおこなえるようになりました。

なおこのままではあらゆるファイルを編集中に C-c p が効いてしまうので python-mode のフックを利用すると良いでしょう。

```el
(load "python-pep8")
(setq python-mode-hook
  (function (lambda ()
    (local-set-key "\C-c\ p" 'python-pep8))))
    (setq auto-mode-alist (cons '("\\.py$" . python-mode) auto-mode-alist))
    (setq interpreter-mode-alist (cons '("python" . python-mode)
                                       interpreter-mode-alist)))
```

また Emacs ならソースコード編集中に常に PEP8 の適合性検査をおこない、それを可視化することも可能です。これは[別記事にまとめました](http://qiita.com/ynakayama/items/e5ec9b53ae6eeec4fa6e)ので Emacs をお使いのかたはご覧ください。

## まとめ

[PEP8](http://legacy.python.org/dev/peps/pep-0008/) はすべての Python プログラマに必須のソースコードスタイルガイドです。ツールなどを駆使して、なるべくガイドラインに準拠するようにコードを記述するようにしましょう。