アメリエフの技術ブログ

Amelieff Staff Blog

読みやすいPythonコーディングを心がける

プログラマーとして、美しいコードを書くこと、つまり誰でも読みやすいコードを書くことは重要だと思います。
特に複数のプログラマーと共同開発する場合は、開発スピードの向上にも繋がります。

私がよく使うPythonは、元々とても読みやすい言語の1つですが、
コーディング規約に則って書くことで、さらに可読性を高めることができるでしょう。

今日はPythonの美しいコードを書くための、規約コードチェックツールについてご紹介します💡

f:id:iijm-l:20200525142141p:plain

Pythonのコーディング規約: PEP 8

Pythonには、PEP 8と呼ばれるコーディング規約があります。
まず、PEPはPython Enhancement Proposalの略で、Pythonに関連する様々な情報、プロセス、環境の新機能などを説明した文書群です。
PEP 8は、その8番目の文書であり、Pythonのコードスタイルについて記載されています。

PEP 8のルールで思いつくものをいくつか挙げてみます。

1. インデント

インデントは半角スペース4つで統一します。
基本的にタブは使用しませんが、既にタブでインデントされているコードとの一貫性を保ちたい場合はその限りではありません。
Python3では、インデントとしてタブとスペースを混合するとエラーになります。

また、長すぎる行を避けるために括弧内改行を行う場合は、引数を縦に揃えるか、または突き出しインデントを使用します。

# OK
def example1(var):
    print(var)

# OK 
def very_long_name_of_function(var_a,
                               var_b, var_c):
    print(var_a)

# OK  突き出しインデントはインデントのレベルを深くする
def very_long_name_of_function(
        var_a, var_b, var_c):
    print(var_a)

# NG print()とインデントが区別できないため
def very_long_name_of_function(
    var_a, var_b, var_c):
    print(var_a)


2. 一行の長さ

全てのコード行は最大79文字にしましょう、と定められています。長い文は¥を入れて改行しましょう。しかし1. インデントで言ったような、括弧内(関数の引数など)は'¥'不要で改行できます。
コメントは最大72文字です。

# OK
with open('/path/to/some/file/you/want/to/read') as file_1, \
     open('/path/to/some/file/being/written', 'w') as file_2:
    file_2.write(file_1.read())


3. import

import文は一番上にまとめ、各モジュール毎に書きます。しかし、1つのモジュールから複数のclassを呼び出すときは、一行で書いてもOKです。

# OK
import os
import sys

# NG
import os, sys

# OK
from subprocess import Popen, PIPE


4. 空行

各関数の処理内容によっては、どこで空行で区切るか難しいですが、通常の関数定義とclass 定義は空白2行、class内部の関数の定義は空白1行で区切ることが推奨されています。

class A(object):
    # contents of A
    def example1(var_1):
        print(var_1)

    def example2(var_2):
        print(var_2)


class B(object):
    # contents of B


上記のように、誰がコーディングしても一貫性が保たれるようなルールが記述されています。 しかしいざ「ルールを守って書こうじゃないか!」と思っていても、チェックが漏れてしまうこともありますよね。
そんな時のために、コードを自動でチェックしてくれるツールが存在します。


コードチェックツールを使ってみる

様々なコードチェックツールがあります。

  • pep8
  • pyflakes
  • flake8
  • pylint

今回はその1つであるflake8を使ってみます。
flake8は、pep8とpyflakesの両方のチェックを同時に行えるラッパーなので、簡単にいうと検出されるエラーの範囲が広いです。

以下、各種pythonツールは下記の環境で動作確認しています。

  • MacOS Catalina 10.15.2
  • python 3.6.10
  • pip 20.0.2


flake8の使い方

flake8のインストール

以下のコマンドで、インストールします。

$ pip install flake8


テスト用スクリプトの作成

テストのため、わざと規約に反する要素を含んだスクリプト(example.py)を作成しました。

import os, sys

a =  "test"
b = [1,2, 3 ]

class example():

    def print_moji(self, text):
        print( text )
    def print_max(self,l):
        print(max(l))

if __name__ == "__main__":
    EX = example()
    EX.print_moji(a)
    EX.print_max(b)  

どこに出しても恥ずかしいコードですね。


まずは、このスクリプトを通常通り実行します。

$ python example.py
test
3

正常に動作します。
しかしコーディング規約に反する、余計なものだらけです。
読みにくいので、これではチームメイトに呆れられてしまいます。

flake8にチェックしてもらう

このスクリプトをflake8に投げてみます。
使うときは、$ flake8 <入力したいスクリプト>と入力します。

$ flake8 example.py 
example.py:1:1: F401 'os' imported but unused
example.py:1:1: F401 'sys' imported but unused
example.py:1:10: E401 multiple imports on one line
example.py:3:4: E222 multiple spaces after operator
example.py:4:7: E231 missing whitespace after ','
example.py:4:12: E202 whitespace before ']'
example.py:6:1: E302 expected 2 blank lines, found 1
example.py:9:15: E201 whitespace after '('
example.py:9:20: E202 whitespace before ')'
example.py:10:5: E301 expected 1 blank line, found 0
example.py:10:23: E231 missing whitespace after ','
example.py:10:24: E741 ambiguous variable name 'l'
example.py:13:1: E305 expected 2 blank lines after class or function definition, found 1
example.py:16:20: W291 trailing whitespace

ものすごく怒られました...。

flake8の出力には、スクリプト名、何行目、何文字目、エラーコード、その詳細が記載されています。
上記の出力をエラーコード毎にまとめてみると、以下の通りです。

  • E: コードスタイルに関するエラー
    E201: '('の後に余分なスペースがあります
    E202: ']'の前に余分なスペースがあります
    E222: スペースが重複しています
    E231: ,の後のスペースが抜けています
    E302: 2行の空白行が必要ですが、1行しかないです
    E305: class定義または関数定義の後は2行の空白行が必要ですが、1行しかないです
    E401: 複数のimportが一行で行われています
    E741: あいまいな変数名です
    (小文字のエルlは大文字のアイIと間違えやすいので使わないでください)

  • F: 論理的に矛盾したエラー
    F401: importしたモジュールが使用されていません

  • W: 警告
    W291: 末尾に空白があります


修正する

指摘された内容にしたがって、コードを修正しました。

a = "test"
b = [1, 2, 3]


class example():

    def print_moji(self, text):
        print(text)

    def print_max(self, var_list):
        print(max(var_list))


if __name__ == "__main__":
    EX = example()
    EX.print_moji(a)
    EX.print_max(b)


これで、flake8には怒られなくなりました。

$ flake8 example.py
$



まとめ

規約に準ずるコードを書くことで、誰でも読めるようなコードに仕上がります。
ツールなどを駆使して、なるべくコーディング規約に準拠したコードを記述するように心がけましょう!
また、プロジェクトによってコーディングルールが異なる場合もありますので、周りの人と相談しながらコーディングできるとさらに良いですね✨