Python 1 行が 79 文字以内である理由と 1 行を最大 79 文字以内に抑える方法

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

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

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

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


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

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


PEP 8, Limit all lines to a maximum of 79 characters. の抜粋、和訳です。

PEP 8: Python コードのためのスタイルガイド
Maximum Line Length | PEP 8 -- Style Guide for Python Code

すべての行を最大 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.

Markdown の改行について

全く話がそれますが Markdown では改行は、システムに任せると言う考えなので、それとは真逆ですね。個人的には改行はすごく読みやすさに影響するので Markdown よりは Python 的な方がやりやすいのですが..。

PARAGRAPHS AND LINE BREAKS | Markdown: Syntax

段落は単純に1つまたはそれ以上の連続した行の文章で、段落は1つまたはそれ以上の空白で分けれらています。(空白の行とは、空白の行ように見える行、全てを指します。ー空白またはタブを除く何も含まない行は、空白とみなされます。)通常のパラグラフは、空白またはタブでインデントされてはいけません。
A paragraph is simply one or more consecutive lines of text, separated by one or more blank lines. (A blank line is any line that looks like a blank line — a line containing nothing but spaces or tabs is considered blank.) Normal paragraphs should not be indented with spaces or tabs.

「1つまたは2つ以上の連続した文章の行」と言うルールの意味合いを、 Markdown では「強制的に改行された」文章の段落として捉えています。これはほとんどの text ファイルから HTML ファイルへの書式整形を行うツールと大きく異なります。(Movale Type の “Convert Line Breaks” オプションも含めて)ほとんどの書式ツールというのは、段落内の全ての改行文字を <br /> tag に変換します。
The implication of the “one or more consecutive lines of text” rule is that Markdown supports “hard-wrapped” text paragraphs. This differs significantly from most other text-to-HTML formatters (including Movable Type’s “Convert Line Breaks” option) which translate every line break character in a paragraph into a <br /> tag.

あなたが Markdown を使用していて <br /> tag を挿入したいときは、行を2つまたはそれ以上の半角スペースを入力した後に改行を入力してください。
When you do want to insert a <br /> break tag using Markdown, you end a line with two or more spaces, then type return.

もちろん、<br /> を生成させるために、このようにして空白を2文字以上いれることは手間となります。しかし、単純に「全ての改行は <br /> だという。」という考えは、 Markdown ではうまく行きません。Markdown の email 形式の blockquoting と multi-paragraph list items なら、うまく書式整形されて見栄えも良いです ー 行末に達すると強制的に改行される機能(hard breaks)を使って書式整形を行うときは(訳注: 具体的にどういう時にうまく行くのかわからない..)。
Yes, this takes a tad more effort to create a <br />, but a simplistic “every line break is a <br />” rule wouldn’t work for Markdown. Markdown’s email-style blockquoting and multi-paragraph list items work best — and look better — when you format them with hard breaks.

2. 抽象化することを前提にしているため

Python には iterator, descriptor など、コードを抽象化できる様々な機能があります。これらの機能を使うことにより、比較的短く書くことできたりする場合あったりします。

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


実は 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. 抽象化の度合いあげる。

例えば、iterator, operator overload を使って短くすることができます。method も抽象化の技法の1つかなと思ったりもします。

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

Python のイテレータ

5. 変数名を短くしちゃう?

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

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

変数名が長い

  • メリット 読めばわかる。
  • デメリット 読みたくない(読めばわかるが)

変数名が短い

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


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

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

使われる頻度が頻繁にあるなら、たとえスコープが広くても、短くていいと思います。例えば、組込関数や組込型なんかは、総じて短い名前です。

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

5.3. なんでこんなことを考えたか

Qiita の記事ですごく感情的なコメントが多い記事を見つけました。
なぜfor文は禁止なのか?関数型記述のススメ - Qiita

なぜこんなに感情的になったのかは、いくつか要因があると思います。主たる要因は for 文を禁止にはできないと言うことかなと思います。

おそらく Python に言いかえれば、リスト内包表記は、読みやすいと言うのに近い主張かなと感じました。しかし、リスト内包表記が読みやすいからと言って for 文がいらないわけではないと思います。

また、副次的な要因としては、関数名が長すぎることだと思います。"100 以下の偶数の合計を計算する" と言う、わかりきった処理に対して長い関数名を当てています。

// 元のコード
const from0To100Array = Array.from(Array(100).keys());
const isEvenNumber = i => i % 2 === 0;
const addAll = (total, i) => total + i;
const totalOfEvenNumberUnder100 = from0To100Array.filter(isEvenNumber).reduce(addAll);


変数名を短くすると精神的な負荷が、低いコードになります。isEvenNumber は Number は自明なので isEven に。addAll は All は加算していないので add に。

// 少し書き換えてみる。
const array100 = Array.from(Array(100).keys());
const isEven = i => i % 2 === 0;
const add = (a, b) => a + b;
const sum100 = array100.filter(isEven).reduce(add);


① 適用できる範囲が本来はもう少し限定されるのに for 文はいらないと言ってしまったのと、② 取り上げる例を、もう少し業務ロジック的なもの、変数名が長くなるものを取り上げていれば、温度感も、もう少し違ったのかなと思ったりもします。