◆ Autopep8, Black, Yapf

Python はフォーマッタの必要性薄め

Python ってインデントがコード上必須だし たいしてフォーマッタの出番がないと思います
他言語だと他のところからコピペしてきたあとにフォーマット書けてインデント揃えてから修正したりしますが Python だとそれは難しいです
インデントが崩れたものを自動で揃えることができません
C 言語系の {} でブロックを作る言語で {} を消したら復元できないのと一緒です
if などの : の次の行のインデントを深くできても どこで浅くするかの判断ができないです
仮に自動でそれっぽくしたとしてもそれが間違ってる可能性も十分あってそれを見つけるのって書いた人でもけっこう難しいです
処理内容を 1 行 1 行理解した上でこれは条件分岐やループの中の内側にあるべきかをチェックすることになりますから

入れてみる

その結果必要なのはインデントは揃ってるものの変なスペースがあちこちに入ってるようなコードのスペース調整や リストやディクショナリの折り返しくらいだと思います
それだけとは言っても必要なときがあるのでフォーマッタを入れてみることにしました

種類

調べたところ基本この 3 種類のようです


VSCode の設定を見ると 3 つとも対応してました
というかそれぞれ名指しでパス指定とかできるくらいで Python の拡張機能を入れていれば用途ごとの有名なライブラリはググらなくても拡張機能の対応ライブラリを見るのが良さそうです
テストについても有名ライブラリは名前付きで設定項目にありました

それぞれを調べてみたら Autopep8 は名前通り PEP8 準拠のコードになるよう自動で修正してくれるというもので Black は PEP8 をさらに厳しくしたルールでフォーマットするもので Yapf は自由度高めに設定できるフォーマットのようでした
PEP8 に準拠したいなら Autopep8 か Black を使えばよさそうです

Black は PEP8 のサブセットなので PEP8 は満たせてます
設定少なく強制するあたりは JavaScript の Prettier に近いものを感じます
Prettier は高速でグチャグチャになったコードや minify 済みでも改行したりしていい感じにしてくれるので良い所も多いのですが アップデートでフォーマットの仕様を変更したのに前のに戻すオプションをつけなかったり 条件演算子を複数行で書くときにインデントをつけず全部縦に揃えるとかありえないようなフォーマットを強制してきたりもするので Black もそういうのがありそうなのがちょっと心配です
タブインデントを許可しないくらいほどで Prettier よりも選択肢はなさそうです
↑の issue はタブの有用性について説明してくれてるいい issue だと思うんですけどね

Yapf では自由に設定できるあたり JavaScript の ESLint に近そうです
それはそれで設定が面倒そうなので準備段階でもういいやってなりそうなので無難に Autopep8 が一番なのかもしれません

PEP8

PEP なので Python 公式なものとは聞いてましたが インデントはスペース 4 つと書いてるのを見た時点で残りはちゃんと読んでませんでした
スペースインデントでも勝手にやってくれて あとからタブで書いたものでも自動で適切なスペースのインデントに修正してくれて さらにスペースが 3 つや 5 つのものがあった場合に自動で 4 の倍数にしてくれるのならありなのかもしれません

他にはどういうものがあるのか見てみました
非公式ですが日本語版があったのでこっち見ました
https://pep8-ja.readthedocs.io/ja/latest/

思ったよりどっちでもいいものが多かったです
{} の内側にスペース入れるかどうかは Prettier で入れられるのに見慣れてしまいましたが Python だと入れないほうが主流なのか PEP8 的には入れないことになってました
自分では書かないですが勝手に揃えてくれる分にはどっちでもいいです

気になったのは = の前後のスペースについてです
基本はスペースありなのに関数定義時のデフォルト引数と名前指定で引数を渡すときの = だけスペースなしです
なのにアノテーションがある場合にはスペースありです
統一感ないしわかりづらいので全部スペースありにすればいいのに
……と思ったものの 考えてみたら私も昔からの癖で for 文で初期値や条件式書くところには演算子の間にスペース入れない書き方してました (そういえば Python だとこのタイプの for 文は構文上ないですね)

あとは行の文字数が 79 文字までなところです
昔のディスプレイならともかく今の時代で 80 字までしか表示できないなんてまずないです
ただでさえ Python は折返しのときに \ を末尾につけるという見づらいものになるので折り返しは積極的にしたくないです
基本は 100 - 120 文字くらいで 最後まで見る必要なくて折り返さないほうが見やすい文字列とかなら折り返しなしの無制限でいいと思ってます
Prettier 使ってても改行は自分でわかりやすいようあえて改行したりしなかったりしてるのにそれを無視されて 1 行にされたり複数行にされるのが困るところです

Autopep8 使ってみた

とりあえず使ってみました
とは言っても Python ではあんまり長いコード書いたりしてないので数十行くらいのスクリプトいくつかに試したくらいです

差分を見ると タブ→スペース 変換と 1 行が長い部分の折り返しくらいでした
長いコードでも折り返される部分とされない部分があるようでした
[x for x in [] if x] みたいな記法で普通の代入の右辺だと折り返されたのに for 文の in の右側だと折り返されませんでした
また 文字列のクオートが ' と " で混在していた場合は特に一方に合わせるわけではなくそのままでした

スペース変換については 全部がタブなら問題なくて 3 つや 5 つの場合に 4 つにしてくれる機能もありました
ただスペースインデントにフォーマットしたあとでタブで書いた場合にそれをスペース化してくれるのは部分的でした

def foo():
a = 1
b = 2

def bar():
x = 1
y = 2

このコードで foo 関数の a がスペースインデントで b がタブインデントだった場合 b がスペースインデントに修正されます
しかし bar 関数の x がタブインデントで y がスペースインデントだった場合に x はタブのままです
インデントが変わって最初がタブインデントの行だとだめみたいです
その行以降が修正されないです
タブインデントとスペースインデントが混ざってると実行時にエラーになるのでこのままだと使えません

また修正されてる行もタブ 1 つが 4 スペースに対応してるわけではないようです
多段インデントから浅くなるところにタブインデントの行を配置したら 2 段インデントされた状態になりました
タブはスペース 8 文字扱いされてるようです

フォーマットでスペースに揃えるなら最初からスペース入力にしておいたほうが良さそうです
VSCode なら開いてるフォルダや言語ごとに設定を変えられるので Autopep8 を使うならスペースインデントを設定しておいた方が良さそうです

Python にのみ設定なら settings.json でこれを追加します

{
"[python]": {
"editor.insertSpaces": true
}
}

外側で editor.insertSpaces が false と書いていても Python の場合はこっちが優先されます