# PEP 8 ってなに?

Python のコードは
こうやって書こうね
と、きめた決まりごとです

例えば、4文字のスペースでインデントを作ったり...

# NG 2 文字のインデント
def insertion_sort(lst):
  for i in range(len(lst)):
    for j in range(i):
      if lst[j] > lst[i]:
        lst[j], lst[i] = lst[i], lst[j]
# OK 4 文字のインデント
def insertion_sort(lst):
    n = len(lst)
    for i in range(n):
        for j in range(i):
            if lst[j] > lst[i]:
                lst[j], lst[i] = lst[i], lst[j]

頭文字を大文字にしてクラス名を作ったり...

# NG 全て小文字
class user(object):
    def __init__(self, name):
        self.name = name
# OK 頭文字は大文字
class User(object):
    def __init__(self, name):
        self.name = name

メソッドを定義する、クラスの中で関数を定義するとき、関数の第一引数を self と名付けたり...

# NG 第一引数が self ではない
class Cat(object):
    def __init__(cat, name):
    	cat.name = name
# OK 第一引数は self
class Cat(object):
    def __init__(self, name):
        self.name = name

何気なく従っているこの書き方は、 PEP 8 という文書で定められています。

有志の方が訳してくれたありがたいものが、こちらにあります。

# 1. はじめに

# ◯ この記事は、なにを説明してくれるの?

class 定義文を教わる時に第一引数は self と書くように説明されます。 しかし、別に self と書かなくても動くのに、 なぜ self と書くのかは説明してくれません。

self と書くのは PEP 8 という文書で規定されています。 PEP 8 とは何者でしょうか? そしてどれくらい従わないといけないものなのでしょうか? そう言った温度感について、この記事ではご説明させていただきます。

もう少し具体的に言えば、この記事は PEP 8 の冒頭の数行を解説するだけです。 この冒頭の数行が地味に難しくて、PEP 8 がなんの文章か理解できず長いこと放置してました。

はじめに
この文書は Python の標準ライブラリに含まれている Python コードのコーディング規約です。

CPython に含まれる C 言語のコード [1] については、対応する C 言語のスタイルガイドを記した PEP を参照してください。 この文書と PEP 257 (Docstring 規約) は、Guido が書いたオリジナルの Python スタイルガイドのエッセイと、 Barry のスタイルガイドに少し追記したものをまとめたものです。 [2]

PEP 8 - Python のコードスタイル

# ◯ PEP 8 を読むときのポイント

まず 使ったことのない機能と言葉は、読み飛します。 もっと言えば、わからないところは読み飛します。 使ったことの無い機能のコーディング規約は理解できませんからね。

また PEP 8 の文章を 急いで覚える必要は、無いかなと思います。 実際に PEP 8 に沿って書く必要が生じたとしても、 大抵 エディタがここが間違ってるよと指摘してくれます。

# ◯ PEP 8 ってなに?

答え: Python のコーディング規約です。

この文書は Python の標準ライブラリに含まれている Python コードの  コーディング規約  です。
This document gives coding conventions for the Python code comprising the standard library in the main Python distribution.
PEP 8 - Python のコードスタイル

# 2. コーディング規約ってなに?

答え: 「色々な書き方ができるけど みんな こうやって書こうね」 と決めた決まりごとです。

なんでこんなことを決めたかというと、 書き方を決めた方が読みやすくなるからです。

Guido の重要な洞察のひとつに、 コードは書くよりも読まれることの方が多い、 というものがあります。 この文書で示すガイドラインの目的は、 コードを読みやすくするとともに、 Python で書かれた幅広いコードのスタイルを一貫させることです。 PEP 20 にもあるように "可読性重要" です。
一貫性にこだわりすぎるのは、狭い心の現れである - PEP 8

# ◯「文法」と「コーディング規約」の違い

「文法」は、機械が読むための決まりごと。 「コーディング規約」は、人間が読むための決まりごとです。

「文法」が間違っていると、機械が読み取れない訳ですから、 SyntaxError になり実行できません。 「コーディング規約」は間違っていもエラーになりませんし、 実行もできます。

「文法」に比べると「コーディング規約」は、緩い決めごとです。 人間なので好き嫌いがあり好みが分かれるので、 そう言った細かい書き方については、 個々の組織やプログラマに任されていると言う訳です。

機能ではなく、決まりごとなので、別に従わなくてもエラーにはなりません。 例えば第一引数を self にするという決まりが PEP 8 にありますが

インスタンスメソッドのはじめの引数の名前は常に self を使ってください。
PEP 8 - Python コードのスタイルガイド

例えば、「コーディング規約」に違反して、 メソッドの第一引数を self にしなくてもエラーにはなりませんが...

class User(object):
    def __init__(user, name):  # <- 第一引数を self にしない。
        user.name = name

user = User('Marcus Vipsanius Agrippa')
>>> class User(object):
...     def __init__(user, name):
...         user.name = name
...
>>> user = User('Marcus Vipsanius Agrippa')
>>>

反対に「文法」に違反すると SyntaxError になります。

class User(object):
    def __init__(self, name)  # <- セミコロンをつけない。
        user.name = name

user = User('Marcus Vipsanius Agrippa')
>>> class User(object):
...     def __init__(self, name)  # <- セミコロンをつけない。
  File "<stdin>", line 2
    def __init__(self, name)  # <- セミコロンをつけない。
                                              ^
SyntaxError: invalid syntax
>>>

ところで、みんな って誰でしょうか? これは「標準ライブラリ」を書く人たちのことを指しています。

問題

機械が読むための決まりごとで、 間違えると機械が読み取れないため実行できないのは、どっち?

問題

人間が読むための決まりごとで、間違えても機械が読み取れるため実行できます。

# 3. ライブラリってなに?

答え: import すれば使える機能

この文書は Python の標準 ライブラリ に含まれている Python コードのコーディング規約です。
This document gives coding conventions for the Python code comprising the standard library in the main Python distribution.
PEP 8 - Python のコードスタイル

例えば math は、ライブラリです。

import math

print(math.sqrt(2)) # 1.4142135623730951

copy も、ライブラリです。

import copy

class User(object):
    def __init__(self, name):
        self.name = name

user = User('岩倉玲音')

new_user = copy.copy(user)

「ライブラリ」と言う単語は、公式ドキュメントの中でなされていませんが、 他のプログラミング言語でも使われる一般的な用語なので、 このような理解でいいかなと思っています。

ライブラリ - Wikipedia
ライブラリ(英: Library)は、汎用性の高い複数のプログラムを再利用可能な形でひとまとまりにしたものである。

また「ライブラリ」は「モジュール」と「パッケージ」に分けることができます。

ひとつのスクリプトファイルは モジュール として扱うことができます。 複数のモジュールをまとめて パッケージ として扱うことができます。
パッケージとモジュール - とほほの WWW 入門

「モジュール」と「パッケージ」については import の箇所で説明させていただきました。

# 4. 標準ライブラリってなに?

答え: インストールしなくても、最初から import できる機能

この文書は Python の 標準ライブラリ に含まれている Python コードのコーディング規約です。
This document gives coding conventions for the Python code comprising the standard library in the main Python distribution.
PEP 8 - Python のコードスタイル

「標準ライブラリ」とは、 Python をインストールした時に一緒にくっついて来る「ライブラリ」を指しています。 「標準ライブラリ」とは、pip install とか GitHub などからダウンロードしなくても、 使える「ライブラリ」と言うことです。

例えば、copy, math は Python さえインストールすれば使えるので「標準ライブラリ」です。 いっぽうで CSV を操作するときに使う pandas やスクレイピングするときに使う requests などは、 pip install しないと使えないので「標準ライブラリ」ではありません。

「標準ライブラリ」という言葉も他の言語でも使われる一般的な用語なので、 覚えておいても損はないかなと思います。

標準ライブラリ - Wikipedia
プログラミング言語における標準ライブラリとは、 通例的に言語の各実装に備えられているライブラリである。 いくつかの場合では、ライブラリはプログラミング言語の仕様において直接言及され、 その他の場合では、標準ライブラリの内容はプログラミングコミュニティでの より非公式な慣例において決定される。

ちなみに Python の「標準ライブラリ」の一覧は、公式サイトのドキュメントにあります。

# 5. みんなが守ってる PEP 8

PEP 8 は、標準ライブラリを開発するためのコーディング規約と書かれています。 しかし Python プログラマは、 ある程度 "みんな" が PEP 8 に準拠してコードを書いている 気がします

まず、8 割の GitHub にある Python のスクリプトが 半角スペース 4 文字でインデントされています。 ちなみに下の結果で 1 と言うのは、タブらしいです。

Pythonのインデントは何が正解なのか - Qiita
spacenumber
6 145
4 63121
3 1102
2 6725
1 9441

また、メソッドの第一引数が self と書かれていない Python のコードを僕はサンプルコード以外では見たことがありません。 みんな右も左も分からないうちに PEP 8 で書くように教えられるんですよね。

実際 PEP 8 は、標準ライブラリのためのコーディング規約のはずですが、 それ以外の組織ないしコードについても暗に言及しているような箇所が見受けられます。

Python の標準ライブラリは保守的なので 1行を79文字以内に限定しなければなりません。 (注釈: 標準ライブラリ以外のコードは、 79文字でなくても良い場合があることを示唆しています。 前後の文章を合わせて読むと、もう少しニュアンスが伝わりやすいかなと思います。)
The Python standard library is conservative and requires limiting lines to 79 characters (and docstrings/comments to 72).

# ◯ なんで、みんなが守るようになったの?

答え: Python の「1つだけ」の正解を求めると言う文化的な背景があるような気がします。

例えば PEP 20, The Zen of Python は、その考えを明文化してくれています。

1つあるはずだ -- そして望ましくはたった1つだけ -- それをやる明白なやり方が
There should be one-- and preferably only one --obvious way to do it.
The Zen of Python - PEP 20

# 例 1. 機能で書き方を統一する。

まず Python という言語は、書き方を強制する言語です。 他の言語では「コーディング規約」で定めている書き方であっても、 Python では機能で強制したりします。

例えば、インデントを「コーディング規約」ではなく「文法」で強制しています。 他にもドキュメントを書く位置を「コーディング規約」ではなく機能で強制しています ドキュメントについては、後述します。

(調べて見たら、ドキュメントを書く位置については「文法」では規定されていませんでした。 Python が単純にクラス定義文、関数定義文の先頭にある文字列を docstring として認識しています。 すいません。なに言ってるか、わからないですよね...)

# 例 2. 標準ライブラリとして1つのものを選定してしまう。

また Python の標準ライブラリは、 公式で1つのものを決めてしまうと言う性格を持って保守、運用されているような 気がします。 個々のサードパーティがそれぞれ思い思いに好きなライブラリを開発すると言うのとは反対の考え方です。

10.12. 電池付き - The Python Tutorial

Python には "電池付き" という考え方があります。 Python の大きな標準ライブラリが提供してくれる洗練され安定した機能に、 この "電池付き" という考えが最もよく現れています。
例えば:

Python has a “batteries included” philosophy. This is best seen through the sophisticated and robust capabilities of its larger packages.
For example:

「電池付き」と言うのは、イストールしたらすぐに使えることを指しています。 Python には最初から、ちゃんとした標準ライブラリが、はいってるからすぐに使えるよ、と言うことです。

「電池付き」と言う表現は、電化製品を買ったら電池付きですぐに使えると言うところから持ってきたようです。

かろうじて使ったことが、ありそうなものを抜粋しました。 さきほど登場した、パッケージ、モジュールという言葉が登場します。

10.12. 電池付き - The Python Tutorial

例えば:

  • json パッケージ は、json と言う人気のある中間データ形式の解析する、機能を提供します。 csv モジュール は、よくデータベースやスプレッドシートで使われる csv 形式で書かれたファイルを直接読み書きする機能を提供します。

For example:

  • The json package provides robust support for parsing this popular data interchange format. The csv module supports direct reading and writing of files in Comma-Separated Value format, commonly supported by databases and spreadsheets.

# ◯ 絶対に守らないといけないものではない

とは言え PEP 8 は、絶対に守らないといけない文章ではありません。 PEP 8 にある 「一貫性にこだわりすぎるのは、狭い心の現れである」 を、 ぜひ読んで見てください。 どのようなケースで PEP 8 から外れても良いのか、記載されています。

# 標準ライブラリ bisect

実際、どのくらいの温度感なんだろう?という疑問もありますが。 「絶対やってはいけない」と書かれている書き方も、 標準ライブラリの中にはあったりしますので、 そこまでこだわらなくても良いかな.. と。

def insort_right(a, x, lo=0, hi=None):
    ...

        if x < a[mid]: hi = mid  # <--- ここ!
        else: lo = mid+1

    ...

if/for/while と 短い文を同じ行に置くことが OK な場合もありますが、 複合文を置くのはやめてください。また、複合文でできた長い行を折り返すのもやめましょう!

やらない方が良い:

if foo == 'blah': do_blah_thing()
for x in lst: total += x
while t < 10: t = delay()

絶対やってはいけない:

if foo == 'blah': do_blah_thing()
else: do_non_blah_thing()

try: something()
finally: cleanup()

do_one(); do_two(); do_three(long, argument,
                            list, like, this)

if foo == 'blah': one(); two(); three()

# 標準ライブラリ unittest

あとテストツールを提供してくれる標準ライブラリ unittest も PEP 8 の規約から外れています。

unittest ユニットテストフレームワークは元々 JUnit に触発されたもので、 他の言語の主要なユニットテストフレームワークと同じような感じです。
> unittest - Python 標準ライブラリ

おそらく Java の雰囲気でコードを書いてしまったのだと思います。 JUnit というのは Java のツールです。

# ◯ おじいちゃんの昔話

「みんなの Python」という昔からある有名な Python の入門書があります。 10 年くらい昔は、ほとんど日本では Python を使われていなかったので 「みんなの Python」ってタイトルを見る度に「みんなって... 誰... ?」って 寂しい思いをしてました。

しかも、ほとんどいまみたいにウェブ上に資料もありませんでした。 辛うじてあった Python が好きな人のブログのタイトルが 「偏った言語信者の垂れ流し」だったりしてました。 宇宙の片隅で人がいるのを見つけたような、 物凄い親近感を感じたのを覚えています笑

問題

PEP 8 は元々は何を書くためのコーディング規約でしたか?

# 7. ツールの紹介

コーディング規約、ドキュメント規約に関連したツールを紹介します。 実際に利用する際には、ツールとエディタを連携させることになるかと思います。

ただ、ここではツールとエディタをど連携させる方法については、記載していません。 こういうのもあるんだなー程度に流していただければと思います。

ツールは、誤りを指摘してくれるもの linter と、誤りを修正してくれるもの formatter の2つに分けられます。 また各々 PEP 8 だけに準拠するものと、PEP 8 以上のことをしてくれるツールに分けられます。

# 7.1. linter - 誤りを指摘してくれるツール

このようなツールはよく linter と呼ばれているのを目にします。 C 言語のツール lint が起源かと思われます。

lint とは、主に C 言語のソースコードに対し、 コンパイラよりも詳細かつ厳密なチェックを行うプログラムである。
lint - Wikipeda

linter 周りはこのサイトも参考になります。

# 7.1.1. pycodestyle

PEP 8 に違反するコードがあった場合に指摘してくれます。 設定で変更できるのは、パッと見た限り各規約の有効、無効のみです。 もともと pep8 という名前だったのが Guido のコメントで pycodestyle に変わったらしいです。 Guido がコメントするということは、オフィシャルに近いツールなのかな。

# 7.1.2. Pylint

PEP 8 以外の規約についても、より詳細に設定できます。 最初にこれを導入しようとしたのですが、色々細かくてよく分からないので、 pycodestyle に逃げました笑

# 7.2. formatter - 誤りを自動的に修正してくれるツール

このようなツールはよく formatter と呼ばれているのを目にします。

# 7.2.1. black

PEP 8 の文章でこれが長いこと理解できませんでした。

式や文中の空白文字 - PEP 8

代入(や他の)演算子を揃えるために、 演算子の周囲に 1 つ以上のスペースを入れる

良い:

x = 1
y = 2
long_variable = 3

悪い:

x             = 1
y             = 2
long_variable = 3

書籍「リーダブルコード」では「悪い」方の書き方が推奨されていました。 それに大抵、高校受験などの数学の講師は、大抵 = を揃えて書きます。

なぜ Guido がイコール = を推奨しているのか、わかりませんでした。 この文章を読んでやっと腑に落ちました。

もう Python の細かい書き方で議論しない。black で自動フォーマットしよう

  • 「ここで改行するほうがキレイで良いと思います」
  • 『いや、私はこちらのほうがキレイ良いと思います』

コードレビューでこういう議論をしたことはありませんか? 大切なことだとは思いますが、生産性にはあまり直結しません。 議論を避けるために書き方を決めるほうが良いでしょう (個々の問題について逐次議論するのがエネルギーを無駄にしてしまいます。 一度決めて、再利用するようにしたいものです)。

おそらくそういった細かいところで議論したくないという思いがあるのもしれません。 Black は、設定できる項目が、あまりないそうです。 そういう細かいことで議論はさせない、消耗させない、そういう意図なのかもしれません。

[MUST] 問題があり、必ず治す [IMO] 意見、緩やかな指摘。自分ならこう書くけどどう? [nits] ほんの小さな指摘。インデントやタイポ

たしかに普段のコードレビューでも、特に意識せずこれらは使い分けていた。 こうしてラベルとして明示することでレビュイーに意図が伝わりやすい。 レビュアーもこうした観点からレビューを行える。 なによりレビュアー・レビュイー双方にとって明確に、指摘事項に優先順位がつけられる。 こうすることでレビューとその後のアクションをよりスムーズにすすめる手助けになっている。

コードレビューのポイント

# 7.2.2. YAPF

autopep は PEP 8 に順じたコードを整形してくれます。 YAPF は設定に応じて、それ以上の箇所を修正してくれるそうです。 Google が開発中です。

YAPF - GitHub

Most of the current formatters for Python --- e.g., autopep8, and pep8ify --- are made to remove lint errors from code. This has some obvious limitations. For instance, code that conforms to the PEP 8 guidelines may not be reformatted. But it doesn't mean that the code looks good.

YAPF takes a different approach. It's based off of 'clang-format', developed by Daniel Jasper. In essence, the algorithm takes the code and reformats it to the best formatting that conforms to the style guide, even if the original code didn't violate the style guide. The idea is also similar to the 'gofmt' tool for the Go programming language: end all holy wars about formatting - if the whole codebase of a project is simply piped through YAPF whenever modifications are made, the style remains consistent throughout the project and there's no point arguing about style in every code review.

The ultimate goal is that the code YAPF produces is as good as the code that a programmer would write if they were following the style guide. It takes away some of the drudgery of maintaining your code.

Auto formatters for Python - Medium

All of the formatters are doing a good job at formatting the code. But in my opinion, autopep8 is not really formatting but more or less just trying make your code compliant to pep8. Still, the code might look bad and does not fulfill the requirement of being an auto formatter.

# 7.2.3. autopep

古くからあるツールで PEP 8 に準拠したコードを吐くようにしてくれます。 pycodestyle で PEP 8 に沿うコードを書く時に、元からあったコードは、この autopep に書き換えてもらいました。

$ autopep8 --in-place --aggressive sample.py

# 8. そのほかのコーディング規約

PEP 8 以外にもコーディング規約がいくつかあります。少し覗いてみたいと思います。

# 8.1. Google のコーディング規約

Google のコーディング規約があります。 ほとんど PEP 8 に準拠していて、より詳細に煮詰めて、 さらになぜそうしないといけないのか、についても言及してくれていて、 とても面白いです。

下記の文章は、自分も全く理解していませんが、難しい機能は使わずに、 ベタ書きで実装した方が読みやすかったりするから注意してな、 的なことが書かれています。

Google Python スタイルガイド - Kosei Kitahara's Works

強力な機能
このような機能は避ける。

定義:
Python はとても柔軟な言語であり、 メタクラス、バイトコードへのアクセス、 高速コンパイル、動的な継承、オブジェクトの親の変更、インポートハック、 リフレクション、内部システムの変更 など多くの素敵 (変態的) な機能があります。

利点:
これは言語の強力な機能です。これはコードをさらに軽量にできます。

欠点:
このような機能を必要としない場合においても、これら素敵な機能は非常に魅力的でしょう。 しかし、このような機能を含むコードはとても可読性が低く、分かりづらく、 デバッグが難しいでしょう。 この方法は最初は高速 (オリジナル作者にとって) ですが、 コードを再確認する必要がある場合、 より長いコード (標準的なコード) の方が読みやすくメンテナンス性にすぐれている傾向があります。

結論:
このような機能は避けましょう。

# 8.2. Chromium のコーディング規約

この規約が面白いのは過去に 2 文字のスペースを採用していたことです。 これから始まる新しいプロジェクトについては、4 文字で書くように示されています。

Python Style Guidelines - The Chromium Projects
Historically, we adopted a style that was congruent with Google internal Python style guide (2 spaces with MethodsAsCamelCase).

Chromium - Wikipedia
Chromium(クロミウム)はオープンソースのウェブブラウザのプロジェクト。

Edge ブラウザがついに Chromium を採用へ  Mozilla は「独占は危険」と警告
Windows 10 のデフォルトブラウザ「Microsoft Edge」が大きな転機を迎えました。 Microsoft が Edge ブラウザを、オープンソースの「Chromium」ベースに変えると発表したのです。 Edge がこれまで採用してきた独自の HTML レンダリングエンジン「EdgeHTML」は、Chromium が使っている「Blink」に切り替わります。 新しい Edge のプレビュー版は 2019 年初頭にも配信される見込みです。

# 8.3. Ruby のコーディング規約

Ruby は、コーディング規約が標準では定められてはいません。 プログラミングそのものを楽しみたいと言うのがコンセプトだった気がするので、 こう言うスタイルがいいのかなと思ったりもします。

コーディング規約を自分で決めるのは大変ですが、 そういったことも含めて楽しんでほしいというのが意図なのかなと、 個人的に思ったりもします。

まつもと : コーディング規約を決めてくれないと仕事できないよ!っていうような人もいて、 「君、本当に仕事してる?(笑)それは自分で考えようよ」と思っちゃう。
Ruby コーディングスタイルの現状と Standard gem のご紹介

# 9. おわりに

以上になります。

ここまで PEP 8 の文章の性格について、説明させていただきました。 PEP 8 をみんなが守っていて「文法」に準じるくらいの権威を持っている 気がします

PEP 8 という言葉そのものは覚えておいても 損はないかもなという感覚がなんとなく伝わっていれば幸いです。 ここまでお付き合いいただき、誠にありがとうございました。