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






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



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



コードレビューする際に、エディタで勝手に改行されると読みづらくなるので。

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


また単純に横に長すぎると読み辛いということかなと思ったりもします。 適切に抽象化してなっていうそういう意味合いかなと思ったりもします。 以下は PEP 7 という Pythonインタープリタ本体を書くときの C 言語のコーディング規約からの引用になります。

  • 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.

PEP 7 - Style Guide for C Code


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

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


あんまり関係ないけど面白いです。

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


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

結構厳しい制限ですが、どれくらいの温度感なのでしょうか? 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


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

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


◯ 和訳

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

1行の最大行数 - PEP 8
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).

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.

バックスラッシュは、時と場合によっては、適切かもしれません。 例えば、長い 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.

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

インデントが 4 文字のスペースである理由
インデントが 2 文字のスペースではない理由



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

インデントには 4 文字の半角スペースを使ってな
Use 4 spaces per indentation level.
PEP 8 - Style Guide for Python Code


ただでさえ 79 文字で制限されているのに 4 文字でインデント作れとか、どんだけ縛りプレイなんや、とか思ったりもしました。

逆にインデントを深くするなということなのかなと、最近、個人的に思うようになりました。 インデントが深くなった処理は関数にまとめて浅くすることができます。

ネストが浅い方が良いというのは「循環的複雑度」という言葉である程度、規定されてるのかな...



f:id:domodomodomo:20180805163623j:plain 尊敬するフリをして煽ってくるスタイル

1 行を最大 79 文字以内に抑える方法

1 行 79 文字と言う制限が、実は結構重くて最初は無視するような設定にしようかと思っていたのですが、最近は遵守しています。いくつか回避方法があります。

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

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


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

· continued lines or strings with \
- use (...) continuation and/or string literal concatenation
Python Regrets

2. 括弧 ( ) を使う

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


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

Python で pep-8 の「E501 line too long」にしないための...
line too longの対処法(メソッドが連続する場合)

3. 変数を分ける

個人的にはこんな感じで別の変数に一旦格納して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')

4. 関数にする

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

  • 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.

PEP 7 - Style Guide for C Code

5. 抽象化の度合いあげる。

例えば、iterator, descriptor, operator overload を使って短くすることができます。

>>> for member in team.member_list:
...     print(member)
川島 永嗣
香川 真司
長谷部 誠 
>>>
>>> for member in team:
...     print(member)
川島 永嗣
香川 真司
長谷部 誠 
>>>

Python のイテレータ

6. 変数名を短くする

もし短くしても、他の方が理解できるなら、短い方が良いと思います。

6.1. 変数名の長さによるメリット、デメリット

変数名が長い

  • メリット 読めばわかる。
  • デメリット 読みたくない(読めばわかるんだけど...)

変数名が短い

  • デメリット 読んでもわからない
  • メリット 読んでもいいかな、と思う


もちろん変数名が長い方が、理解できるコードになります。ただ、その分コードが長くなって、読むのが精神的に辛いコードになってしまいます。

6.2. 変数名の長さを決める判断基準
  1. 使われる頻度
  2. スコープの広さ
  3. コードそのものの理解のしやすさ。

使われる頻度が頻繁にあるなら、短くてもいいのかなと思います。例えば、組み込み関数や組み込み型は、総じて省略された短い名前です。

スコープが狭ければ、短くてもいいのかなと思います。背景を共有している場合、逆に自明なことを記載すると冗長に感じます。

コードの理解のしやすさと言うのは、コードそのものが、よく使われるアルゴリズムなど、わかりきったものであれば、ごく短い変数名を使う方が良いと思います(0 ... 10 までの和のようなものとか)。反対に、業務ロジックなどが書かれたものであれば、ある程度詳細に書いた方が望ましいと感じます。


PEP 8 の文章の性格について、まとめました。サイト移転中です。

https://python.ms/pep8/