Last Updated: 2/6/2024, 5:44:57 AM

なんで 1 行が 79 文字以内で、
インデントは 4 文字なのか?

PEP 8 にそう書いてあるから

すべての行を最大 79 文字に制限する。
Limit all lines to a maximum of 79 characters.
PEP 8 -- Style Guide for Python Code (opens new window)

インデントには 4 文字の半角スペースを使ってください。
Use 4 spaces per indentation level.
PEP 8 - Style Guide for Python Code (opens new window)

# はじめに

なぜ PEP 8 でそのように定められているのかについて調べたり、 考えたりして見ました。 PEP 8 がどのような文章なのかについては、 こちらにまとめさせていただきました。





# 1 行が 79 文字以内である理由

エディタに勝手に
改行させないため。

コードレビューする際に、エディタで勝手に改行されると読みづらくなるから。 79 なのに 80 と書かれている理由は後述します。

この 1 行の文字数の制限は、 1 行が 80 文字のエディタが折り返して表示する機能を避けるために選定されたものである。
PEP 8 - Style Guide for Python Code (opens new window)

また、単純に横に長すぎると読み辛いということかなと思ったりもします。 あまり関係ないですが面白いです。

日経電子版と朝日新聞デジタルは21文字以上40文字未満のあたりにピークが来ますが、 NHKは60文字以上80文字以下を中心に、綺麗なカーブって感じですね。
読みやすい文章の長さとは? 一文の長さを調べてみた。 (opens new window)

以下は  PEP 7  という Python のインタープリタ本体を書くときの C 言語のコーディング規約からの引用になります。

PEP 7 - Style Guide for C Code (opens new window)

  • 4 文字のスペースをインデントとして使い、決してタブを使ってはいけない。
    Use 4-space indents and no tabs at all.

  • 1 行につき 79 文字を超過してはいけない。 もしこの 1 行 79 文字のルールとこの前の 4 文字スペースのインデントのルールでコードを書くのが厳しいならば あなたのコードは複雑すぎるということだ --  関数を使うことを考えてください。 
    No line should be longer than 79 characters. If this and the previous rule together don't give you enough room to code, your code is too complicated -- consider using subroutines.

「関数を使うことを考えてください」というのは、 関数を使って1行が短くなるように書いてくださいということだと思っています。 関数は同じ処理を繰り返しいろんなところで使えるようにするという側面もありますが、 処理に名前をつけて短くするという側面もあるかなと思っています。

# ◯ PEP 7 - C 言語のコーディング規約

標準ライブラリには Python で書かれたものと C 言語で書かれていものがあります。

C 言語で書かれたコードを import して Python で使えるの?という疑問はありますが、 とりあえず、いまは使えるということだけ押さえておいてください。 例えば、標準ライブラリ math は C 言語で書かれています。

C 言語で書かれた標準モジュールには PEP 7 (opens new window) というコーディング規約が存在します。 PEP 8 の冒頭で書かれている「C言語のスタイルガイドを記した PEP」とは この PEP 7 のことを指しています。

CPython に含まれるC言語のコード [1] については、  対応する C 言語のスタイルガイド  を記した PEP を参照してください。
[1] PEP 7, Style Guide for C Code, van Rossum
Python コードのスタイルガイド - PEP 8 (opens new window)

問題

PEP には C 言語で書かれた標準ライブラリに関するコーディング規約がある。

# ◯ なんで 79 と 80 で表記に揺らぎがあるの?

おそらく改行コード \n を含めるか含めないかで文字数が、かわってきます。

text = 'abc\n'
len(text)
print(text)
>>> text = 'abc\n'
>>> len(text)
4         <--- \n で 1 文字として扱う。
>>> print(text)
abc
          <--- 改行されます。
>>> 

\n は2文字なのに1文字として扱うのか?という疑問はあります。 Python は1文字、1文字に数字を割り当てて扱っています。 この割り当ては Unicode という規格に従っています。 割り当てられた数字は、組み込み関数 ord で確認できます。

ord('\n')
>>> ord('\n')
10
>>> 

# ◯ どのくらいの温度感なの?

結構厳しい制限ですが、どれくらいの温度感なのでしょうか? Python で最初からはいっているモジュールで Python で書かれたものは、79 文字以内に限定されています。

Python の標準ライブラリは保守的であり、79 文字以内に限定されていなければなりません (また docstring や コメントは 72 文字以内に限定されています。)
The Python standard library is conservative and requires limiting lines to 79 characters (and docstrings/comments to 72).
PEP 8 - Style Guide for Python Code (opens new window)

ここの PEP 8 に書かれている「標準ライブラリ」とは import すると使える機能のうち Python で書かれたコードを指しています。 具体的には下記の URL 先のコードです。

ただ型アノテーションを使うときは、相当苦しいという意見を見ます。

また Python のコードではないですが Linux のカーネルのコーディング規約も変化しているようです。

# ◯ 80 文字という数字は、どこから来たの?

ちなみにこの 80 という数字はパンチカードから来ているのでは、 という話を聞いたことがあります。

1928年、IBMは縦長の長方形の穴を採用し、 80欄で各欄に12のパンチ位置があり、1欄(コラム)で1文字を表す形式のカードを設計した[23]。
パンチカード - Wikipedia (opens new window)

# ◯ PEP 8 和訳

PEP 8 のうち Maximum Line Length 抜粋、和訳しました。

1行の最大行数 - PEP 8 (opens new window)
Maximum Line Length - PEP 8

すべての行を最大 79 文字に制限する。
Limit all lines to a maximum of 79 characters.

コードの後に続く docstring やコメントのような構造的な制限の少ない長いブロックのテキストについては、 1 行の長さは 72 字以内にするべきだ。
For flowing long blocks of text with fewer structural restrictions (docstrings or comments), the line length should be limited to 72 characters.

エディタのウィンドウを表示する際に必要な幅を制限できれば、 複数のファイルを並べることが可能になり、 2 つのバージョンのコードを隣接して左右に並べて、 コードリビューツールを使うときに効果的である。
Limiting the required editor window width makes it possible to have several files open side-by-side, and works well when using code review tools that present the two versions in adjacent columns.

大抵のツールが提供する、デフォルトで長い 1 行を折り返して表示してくれる機能(wrapping)は、 コードの見た目の構造を破壊し、より理解を困難なものにする。
The default wrapping in most tools disrupts the visual structure of the code, making it more difficult to understand.

この 1 行の文字数の制限は、1 行が 80 文字のエディタが折り返して表示する機能を避けるために選定されたものである。 もし、たとえツールが目印として 1 行の文字を複数行で折り返し表示したときに、 最後の文字に印を置いてくれるような機能がったとしても 勝手に折り返されるのを避けるために1行を 79 文字に制限するべきである。
The limits are chosen to avoid wrapping in editors with the window width set to 80, even if the tool places a marker glyph in the final column when wrapping lines.

いくつかのウェブベースのツールは、自動的に行を折り返してくれるようなことは、全くしてくれないかもしれない。
Some web based tools may not offer dynamic line wrapping at all.

チームによっては、もっと長い行で書きたいと強く思うかもしれない。 この問題について同意に達することができるチームだけが、あるいはそのチームがおもに、保守運営するようなコードに対しては 名目的な文字数の上限を 80 文字から 100 文字に引き上げてもいい(実質な文字数の上限は 99 文字の長さまで伸ばしても良いことになる)、 ただし、コメントと docstring は 72 文字以内のままであると言う条件には、従わねばなりません。
Some teams strongly prefer a longer line length. For code maintained exclusively or primarily by a team that can reach agreement on this issue, it is okay to increase the nominal line length from 80 to 100 characters (effectively increasing the maximum length to 99 characters), provided that comments and docstrings are still wrapped at 72 characters.

Python の標準ライブラリは保守的であり、79 文字以内に限定されていなければなりません (また docstring や コメントは 72 文字以内に限定されています。)
The Python standard library is conservative and requires limiting lines to 79 characters (and docstrings/comments to 72).

# インデントが 4 文字である理由

見やすいから(たぶん)

Google で英語圏のページも調べて見たのですが、見つかりませんでした。

Guido van Rossum 氏が何かしら言及してくれている資料があれば良いのですが、 どのページも「意見」の枠を出ませんでした。 以下、自分の「意見」になりますが、 なぜ Guido van Rossum 氏が PEP 8 においてインデントに4文字を使うという意思決定をしたのかについて、 考えてみたいと思います。

インデントには 4 文字の半角スペースを使ってください。
Use 4 spaces per indentation level.
PEP 8 - Style Guide for Python Code (opens new window)

# ◯ メリットとデメリット

ここではインデントを 2 文字にした場合と 4 文字にした場合に分けて行きます。

  • 2 文字
    • メリット 見にくい
    • デメリット インデントを深くできる
  • 4 文字
    • メリット 見やすい
    • デメリット インデントを深くできない

# ◯ 見やすい

インデントを 2 文字するよりも 4 文字の方が Python のコードを書く時は、見やすいかなと個人的に感じています。 反面、コードを見た時にスカスカな印象もあり、集中しにくい感じがあります。

どちらも一長一短で判断に迷うところです。 Python は、もともと ABC 言語という教育用の言語に影響を受けているため、 集中のしやすさよりも見やすさに寄せたのかなと思ったりもします。

# ◯ インデントを深くしたくないから

ただでさえ 79 文字で制限されているのに 4 文字でインデント作れとか、 どんだけ縛りプレイなんや、とか思ったりもしました。 ちょっとでも if 文, for 文でネストしようものなら、すぐに 79 文字になってしまいます

インデントが 4 文字のスペースである理由、インデントが 2 文字のスペースではない理由は、 「ネストは浅い方が良い」というところから来ているのかなと思いました。

ネストは浅い方が良い
Flat is better than nested.
PEP 20 - The Zen of Python (opens new window)

尊敬するフリをして煽ってくるスタイル

この「ネストが深いと複雑」という考え方は、 サイクロマティック複雑度 (opens new window) という、とても難しそうな言葉に変身して登場してくることがあります。 以下は、応用情報技術者試験の問題について書かれた記事になります。

 「サイクロマティック複雑度(Cyclomatic complexity)」とは、  McCabeによって提唱されたプログラムの複雑さを示す指標です。 「サイクロマチック数」や「循環的複雑度」などとも呼ばれています。 メソッド単位で計測し、高いほどプログラムが複雑ということを示します。 簡単にいえば、  ifやfor, switchなどの分岐が多いほど値が高くなります。 
【H30春AP午後】「サイクロマティック複雑度」の計測方法が全くわからなかったので調べてみたら超簡単だった件 - Qiita (opens new window)

# ◯ なんでタブを使っちゃいけないの?

あとタブを使ってはいけないのは、 パソコンの環境によって表示が変わってしまうからだと思っています。 タブは環境によって、2文字で表示されたり、4文字で表示されたりします。

タブか、スペースか? - PEP 8 (opens new window)
スペースが好ましいインデントの方法です。 タブを使うのは、既にタブでインデントされているコードと一貫性を保つためだけです。 Python 3 では、インデントにタブとスペースを混ぜることを禁止しています。 インデントにタブとスペースを混ぜた Python 2 のコードは、スペースだけを使うように変換すべきです。
Python 2 のコマンドラインインタプリタを -t オプションを付けて呼び出すと、タブとスペースをインデントに混ぜたコードに対して警告を出します。-tt を付けるとエラーになります。これらのオプションの使用を強く推奨します!

# 79 文字以内に抑える方法について考える

以下、3つの方法について考えて行きます。

  1. 名前をつける
  2. 改行する
  3. 抽象化する

# 1. 名前をつける

# 1.1. データに名前をつける - 変数をわける

「変数に値を代入する」ってなんだろう?って改めて考えると、 「値に名前をつける」という側面があることに気づきます。 個人的にはこんな感じで別の変数に一旦格納して2行に分けたりもします。 これはこれで変数が増えてしまうのですが。

# 前
supplier = Supplier.objects.filter(category=supplier_category).order_by('phonetic')

# 後
supplier = Supplier.objects.filter(category=supplier_category)
supplier_ordered_by_phonetic = supplier.order_by('phonetic')

# 1.2. 処理に名前をつける - 関数にする

例えば、ネストが深くなってしまった時は関数にしてネストから外してしまえば良いと思います。

  • 1 行につき 79 文字を超過してはいけない。 もし 1 行 79 文字のルールとこの前の 4 文字スペースのインデントのルールでコードを書くのが厳しいならば あなたのコードは複雑すぎるということだ -- 関数を使うことを考えてください。
    No line should be longer than 79 characters. If this and the previous rule together don't give you enough room to code, your code is too complicated -- consider using subroutines.

PEP 7 - Style Guide for C Code (opens new window)

# 2. 改行する

# 2.1. 括弧 ( ) を使う

括弧内なら改行が許容されます。

def eq(rectangle_a, rectangle_b):
    return all(
        rectangle_a.x1 == rectangle_b.x1,
        rectangle_a.y1 == rectangle_b.y1,
        rectangle_a.x2 == rectangle_b.x2,
        rectangle_a.y2 == rectangle_b.y2,
    )

文字列連結では、こんな書き方ができます。

# 1. str として格納される。tuple に格納される訳ではない。
# 2. + 演算子はいらない。
allowed_chars= ('abcdefghijklmnopqrstuvwxyz'
                'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789')

1行を複数行に分けて書く方法として望ましいものは、 括弧 parentheses ()、中括弧 braces {}、大括弧 brackets [] を使えば Python では暗黙的に行を継続できることを活用することです。
The preferred way of wrapping long lines is by using Python's implied line continuation inside parentheses, brackets and braces.

長い行は、括弧で括られた式によって、複数行に分割させることができます。 バックスラッシュで行を継続させるよりも、括弧を使って行を継続させた方が望ましいです。
Long lines can be broken over multiple lines by wrapping expressions in parentheses. These should be used in preference to using a backslash for line continuation.

# 2.2. バックスラッシュ \ を使う。

def eq(rectangle_a, rectangle_b):
    """2つの長方形が同じかどうかを確認する関数"""
    return \
        rectangle_a.x1 == rectangle_b.x1 and \
        rectangle_a.y1 == rectangle_b.y1 and \
        rectangle_a.x2 == rectangle_b.x2 and \
        rectangle_a.y2 == rectangle_b.y2

バックスラッシュは、時と場合によっては、適切かもしれません。 例えば、長い multiple with-statements は暗黙的に行を継続することができないので バックスラッシュを使うことが望ましいです。
Backslashes may still be appropriate at times. For example, long, multiple with-statements cannot use implicit continuation, so backslashes are acceptable:

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())

(multiline with-statement のインデントに関するさらなる考察は、 上記で議論された multiline if-statement を参照してください。)
(See the previous discussion on multiline if-statements for further thoughts on the indentation of such multiline with-statements.)

他の例としては assert 文があります。
Another such case is with assert statements.

PEP 8 でもバックスラッシュ \ よりも括弧 ( ) を使ってねと書かれてました。 しかし Guido van Rossum 氏は、バックスラッシュ \ は使って欲しくないそうです。 理由は書かれていないですが、確かに汚いですね。 かわりに 括弧 ( ) を使うように言っています。

· continued lines or strings with <br>

# 改行した場合は、適切にインデントをする。

行を折り返した場合は適切にインデントがなさるようにしてください。
Make sure to indent the continued line appropriately.

# 3. 抽象化する。

Python にはコードを抽象化するめに様々なフックが提供されています。 例えば、iterator, descriptor, operator overload などです。 これらを使って短くすることができます。

>>> for member in team.member_list:
...     print(member)
川島 永嗣
香川 真司
長谷部 誠 
>>>
>>> for member in team:  # <-- ちょっとだけ短くできる。
...     print(member)
川島 永嗣
香川 真司
長谷部 誠 
>>>

# 短い名前について考える

書籍「Readable Code」では、変数名の長さについて、 他の人が1度読めばわかるかどうかで、 関数名や変数名の長さを決めるように書かれています。

そのことを踏まえると変数名や関数名を省略して短く書く機会というのは、そんなには無いのかなという気がします。 なぜならフレームワークを自作するような状況しか思いつかないからです。

それでも可読性について議論した記事を読んでいると、 長い変数名は良し悪しに関わらず炎上するのを度々見かけました。 完全に憶測ですが、これだけ炎上しやすいのは、読む側の日常の業務の負担から来るイライラを想起させるからかなと思います。

ここでは、名前を省略しても良い数少ないケースについて、考えて見たいと思います。

# ◯ メリットとデメリット

名前が長い方が、読めば理解できるコードになります。 ただ長い文、読むのが精神的に辛いコードになってしまいます。

  • 変数名と関数名が長い
    • メリット 読めば分かる
    • デメリット 見にくい(読むのが面倒)
  • 変数名と関数名が短い
    • メリット 見やすい
    • デメリット 読んでも分からない

# ◯ 変数名の長さを決める判断基準

  1. 変数が使われる頻度
  2. 共有している情報の程度

# (1) 使われる頻度が頻繁にあるなら

短くてもいいのかなと思います。 関数名や変数名を決める作業は、ドキュメントを読みに行く煩雑さと 関数名を読む煩雑さを天秤にかける作業かなとも感じたりもします。

例えば、組み込み関数や組み込み型は、 総じて省略された短い名前です。たとえば len です。 これが length だったら、書くのも面倒です、読むのも煩雑です。

例えば、逆に、テストコードの関数名とかなら長くてもいいかなと思います。 テストコードの関数は、テストを実施するときの1度だけしか呼び出されないからです。

以下の記事は、テストコードで、コメントを書くくらいなら関数名にしようと主張して少し炎上気味になった記事です。 個人的には筆者に同意したいです。

理由は2つあります。 1つ目は、関数名はコメントに比べて嘘をつきにくいからです。 コードを改修した時に、コードとコメントの内容に乖離があったとしても見落としやすいです。 2つ目は、テストコードの関数名は、テストのフレームワークから呼び出されることはあっても、 自分自身で関数名を書いて呼び出したりすることがないからです。

# (2) 相手と共有している情報がたくさんあるなら

短くてもいいのかなと思います。 例えば、普段会話をするときも分かりきったことなら省略して、伝えたいことだけを伝えた方が理解しやすくなります。

例えば、もしコードが理解のしやすいなら、短くてもいいのかなと思います。 反対に、もしコードが理解しにくいのであれば、ある程度詳細に書いた方が望ましいと感じます。 コードが理解のしやすいと言うのは、0...10 までの和を求めるような普遍性の高いものです。 コードが理解しにくいと言うのは、業務ロジックのような普遍性の低いものです。

以下の記事は 0 ... 100 までの偶数の和を求めるのに、 長い変数名を使い炎上しました。

以下は、上記の記事を読むにあたって、JavaScript が全くよくわからなかったのでまとめた備忘録です。

あとはスコープの狭い名前(ローカル変数)も背景を共有しているので、 スコープの広い名前(グローバル変数)よりも、 短くしても良い可能性があったりするのかなと思ったりもします。

# ◯ まとめ

そのため、もし短くしても他の方が理解できるなら、短く書いた方がいいのかなと思います。 そのため、すでに読み手が理解しているものは、 変数名には記述せずに削除してしまうのが望ましいような気もしたり、しなかったりします。

# おわりに

ここまで以下のように見て来ました。

以上になります。ありがとうございました。