Black コードスタイル

コードスタイル

Black は、一貫性、汎用性、可読性、および git の差分を減らすことを目指しています。類似の言語構造は、類似のルールでフォーマットされます。スタイル設定オプションは意図的に制限されており、めったに追加されません。前のフォーマットは、マジックトレーリングコンマのようなまれな例外を除いて、可能な限り考慮されません。Black が使用するコーディングスタイルは、PEP 8 の厳密なサブセットと見なすことができます。

このドキュメントでは、現在のフォーマットスタイルについて説明します。スタイルの方向性を試してみたい場合は、将来のスタイルを参照して、black --preview を実行してみてください。

Black が行を折り返す方法

Black は前のフォーマットを無視し、コードに均一な水平および垂直の空白を適用します。水平空白のルールは次のように要約できます:pycodestyle が満足するものであれば何でも。

垂直空白に関しては、Black は1行あたり1つの完全な式または単純なステートメントをレンダリングしようとします。これが割り当てられた行の長さに収まる場合は、問題ありません。

# in:

j = [1,
     2,
     3
]

# out:

j = [1, 2, 3]

そうでない場合、Black は最初の一致する外側の括弧の内容を見て、それを別のインデントされた行に配置します。

# in:

ImportantClass.important_method(exc, limit, lookup_lines, capture_locals, extra_argument)

# out:

ImportantClass.important_method(
    exc, limit, lookup_lines, capture_locals, extra_argument
)

それでもまだ収まらない場合は、毎回一致する括弧をインデントして、同じルールを使用して内部式をさらに分解します。一致する括弧のペアの内容がカンマ区切り(引数リストや辞書リテラルなど)の場合、Black はまずそれらを一致する括弧と同じ行に保持しようとします。それがうまくいかない場合は、それらすべてを別々の行に配置します。

# in:

def very_important_function(template: str, *variables, file: os.PathLike, engine: str, header: bool = True, debug: bool = False):
    """Applies `variables` to the `template` and writes to `file`."""
    with open(file, 'w') as f:
        ...

# out:

def very_important_function(
    template: str,
    *variables,
    file: os.PathLike,
    engine: str,
    header: bool = True,
    debug: bool = False,
):
    """Applies `variables` to the `template` and writes to `file`."""
    with open(file, "w") as f:
        ...

データ構造リテラル(タプル、リスト、セット、辞書)または「from」インポートの行が割り当てられた長さに収まらない場合、常に1行に1つの要素に分割されます。これにより、差分が最小限に抑えられ、コードの読者が特定の項目を導入したコミットを見つけることができます。これにより、Black は、既製の black プロファイルまたは手動設定を使用して、isort と互換性を持つようになります。

閉じ括弧は常にインデントが解除され、末尾のカンマが常に付加されることに気づいたかもしれません。このようなフォーマットにより、差分が小さくなります。要素を追加または削除する場合、常に1行だけです。また、閉じ括弧のインデントを解除すると、同じインデントレベルを共有する(上記の例の引数リストとドキュメンテーション文字列のように)コードの2つの異なるセクションの間に明確な区切りが提供されます。

Black はバックスラッシュよりも括弧を優先し、バックスラッシュが見つかった場合は削除します。

# in:

if some_short_rule1 \
  and some_short_rule2:
      ...

# out:

if some_short_rule1 and some_short_rule2:
  ...


# in:

if some_long_rule1 \
  and some_long_rule2:
    ...

# out:

if (
    some_long_rule1
    and some_long_rule2
):
    ...

バックスラッシュと複数行の文字列は、Python 文法で有意なインデントを壊す2つの場所の1つです。バックスラッシュは決して必要ありません。これらは、文法が通常は解析エラーとなるはずの改行を受け入れるように強制するために使用されます。そのため、見た目がわかりにくく、変更に脆くなります。これが、Black が常にそれらを削除する理由です。

バックスラッシュを使用しようとしている場合は、コードを少しリファクタリングすれば、より良い方法があるという明確な信号です。上記の例のいくつかが、それを行うことができる多くの方法を示していることを願っています。

行の長さ

おそらく、独特のデフォルト行の長さに気づいたでしょう。Black のデフォルトは1行あたり88文字で、これは80より10%多くなります。この数値は、80(最も人気がある)または79(標準ライブラリで使用)に固執するよりも、大幅に短いファイルを生成することがわかりました。一般的に、90前後が賢明な選択のようです

記述したコードの行数に応じて支払いを受ける場合は、より低い数値で --line-length を渡すことができます。Black はそれを尊重しようとします。ただし、他のルールを破らずにそれができない場合もあります。まれなケースでは、自動フォーマットされたコードが割り当てられた制限を超えることがあります。

増やすこともできますが、視覚障害を持つ人は100文字を超える行の長さで作業するのが難しいことを覚えておいてください。また、一般的な画面解像度での並列差分レビューにも悪影響を与えます。長い行は、ドキュメントや講演スライドでコードをきちんと表示することも難しくします。

Flake8 およびその他のリンター

リンターの互換性については、他のツールとBlackを一緒に使用する を参照してください。

空行

Black は、不要な垂直空白を避けます。これは、関数内の垂直空白は控えめにのみ使用する必要があると述べている PEP 8 の精神に基づいています。

Black は、関数内では単一の空行を許可し、元のエディターによって残されたモジュールレベルでは単一および二重の空行を許可しますが、括弧付きの式内にある場合は除きます。このような式は常に最小限のスペースに合うように再フォーマットされるため、この空白は失われます。

# in:

def function(
    some_argument: int,

    other_argument: int = 5,
) -> EmptyLineInParenWillBeDeleted:



    print("One empty line above me will be kept!")

def this_is_okay_too():
    print("No empty line here")
# out:

def function(
    some_argument: int,
    other_argument: int = 5,
) -> EmptyLineInParenWillBeDeleted:

    print("One empty line above me will be kept!")


def this_is_okay_too():
    print("No empty line here")

また、関数定義の前後に適切な間隔を挿入します。内側の関数の前後に1行、モジュールレベルの関数とクラスの前後に2行です。Black は、関数/クラス定義と、指定された関数/クラスの直前にあるスタンドアロンコメントの間に空行を配置しません。

Black は、クラスレベルのドキュメンテーション文字列と、その後に続く最初のフィールドまたはメソッドの間に、単一の空行を強制します。これは PEP 257 に準拠しています。

Black は、関数ドキュメンテーション文字列の後に、その直後に内側の関数が開始するために必要な場合を除き、空行を挿入しません。

コメント

Black はコメントの内容をフォーマットしませんが、同じ行のコードとコメントの間には2つのスペースを、コメントテキストが始まる前には1つのスペースを強制します。特定のスペーシングルールが必要な一部のタイプのコメントは尊重されます:shebangs(#! comment)、ドキュメントコメント(#: comment)、ハッシュが長く続くセクションコメント、およびSpyderセル。ハッシュの後の改行されないスペースも保持されます。コメントは、フォーマットの変更のために移動される場合があり、それらに特別な意味を割り当てるツールを壊す可能性があります。詳細については、フォーマット前後の AST を参照してください。

末尾のカンマ

Black は、各要素が独自の行にあるカンマで分割された式に末尾のカンマを追加します。これには、関数のシグネチャも含まれます。

末尾のカンマを追加する例外の1つは、**args、または **kwargs を含む関数のシグネチャです。この場合、末尾のカンマはPython 3.6でのみ安全に使用できます。Black は、ファイルがすでに3.6以上の場合のみを検出し、この状況で末尾のカンマを使用します。その方法が気になる場合は、f-stringと、中に星がある関数のシグネチャでの末尾のカンマの既存の使用状況を探します。言い換えれば、この状況で末尾のカンマを使用したいが、Black がそれを安全だと認識しなかった場合は、手動でそこに配置すると、Black はそれを保持します。

既存の末尾のカンマは、Black に現在の括弧のペアの内容を常に1行に1項目ずつ展開するように指示します。詳細については、以下の プラグマティズム セクションをお読みください。

文字列

Black は、一重引用符('''')よりも二重引用符("""")を優先します。後者が以前よりも多くのバックスラッシュエスケープになることがない限り、後者を前者で置き換えます。

Black は文字列のプレフィックスも標準化します。プレフィックス文字は、大文字の「R」プレフィックス を除いて小文字にされ、Unicode リテラルマーカー(u)はPython 3では意味がないため削除され、複数の文字の場合、「生f文字列」のように、発音どおりに「r」が最初に配置されます。

単一形式の引用符で標準化する主な理由は美しさです。どこにでも一種類の引用符があることで、読者の注意散漫が軽減されます。また、Black の将来のバージョンで、同じ行に並んだ連続した文字列リテラルをマージできるようになります(詳細については、#26 を参照してください)。

なぜダブルクォートを使うのか?それは英語のテキストにおけるアポストロフィを想定しているからです。また、PEP 257で記述されているドキュメンテーション文字列の標準に合致しています。ダブルクォートで囲まれた空文字列("")は、フォントや構文強調表示に関わらず、単一のダブルクォートと混同されることはありません。さらに、文字列にダブルクォートを使用することは、Pythonが頻繁にやり取りするC言語と一致しています。

US英語のような特定のキーボードレイアウトでは、シングルクォートを入力する方がダブルクォートよりも少し簡単です。後者はShiftキーを使用する必要があります。ここでの私のおすすめは、タイプするのが速い方を使用し続け、Blackに変換を処理させることです。

既存の文字列規約(人気のある「データにはシングルクォート、人間が読む文字列にはダブルクォート」など)がある大規模プロジェクトでBlackを採用する場合は、コマンドラインで--skip-string-normalizationを渡すことができます。これは採用支援を目的としており、新しいプロジェクトには使用しないようにしてください。

Blackはドキュメンテーション文字列も処理します。まず、ドキュメンテーション文字列のインデントは、引用符とテキストの両方に対して修正されますが、テキスト内の相対インデントは保持されます。各行の余分な末尾の空白と、ドキュメンテーション文字列の末尾の不要な改行は削除されます。すべての先頭のタブはスペースに変換されますが、テキスト内のタブは保持されます。1行のドキュメンテーション文字列の先頭と末尾の空白は削除されます。

数値リテラル

Blackは、ほとんどの数値リテラルを、構文部分には小文字、数字自体には大文字を使用するように標準化します。0XABの代わりに0xAB1E10の代わりに1e10です。

改行と二項演算子

Blackは、コードのブロックを複数行に分割する場合、二項演算子の前で改行します。これは、BlackPEP 8スタイルガイドの最近の変更に準拠するためです。このアプローチは可読性を向上させると強調されています。

ほぼすべての演算子は単一のスペースで囲まれます。唯一の例外は、単項演算子(+, -, および~)と、両方のオペランドが単純な場合のべき乗演算子です。べき乗の場合、オペランドがNAME、数値CONSTANT、または属性アクセス(チェーンされた属性アクセスは許可されます)のみで、前に単項演算子が付いている場合でも、単純であると見なされます。

# For example, these won't be surrounded by whitespace
a = x**y
b = config.base**5.2
c = config.base**runtime.config.exponent
d = 2**5
e = 2**~5

# ... but these will be surrounded by whitespace
f = 2 ** get_exponent()
g = get_x() ** get_y()
h = config['base'] ** 2

スライス

PEP 8は、スライス内の:を優先度が最も低い二項演算子として扱い、両側に同じ量のスペースを残すことを推奨しています。ただし、パラメーターが省略されている場合は除きます(例:ham[1 + 1 :])。「単純な式」(ham[lower:upper])の場合、:演算子の周りにスペースを入れないことを推奨し、「複雑な式」(ham[lower : upper + offset])の場合は余分なスペースを推奨しています。Blackは、変数名以上のものをすべて「複雑」として扱います(ham[lower : upper + 1])。また、拡張スライスの場合は、パラメーターが省略されている場合を除き(ham[1 + 1 ::])、両方の:演算子に同じ量のスペースを空ける必要があると述べています。Blackはこれらのルールを一貫して適用します。

この動作により、Flake8のようなスタイルガイド適用ツールでE203 whitespace before ':'警告が発生する可能性があります。E203はPEP 8に準拠していないため、これらの警告を無視するようにFlake8に指示する必要があります。

括弧

一部の括弧はPythonの文法ではオプションです。任意の式を括弧で囲んでアトムを形成できます。いくつか興味深いケースがあります。

  • if (...):

  • while (...):

  • for (...) in (...):

  • assert (...), (...)

  • from X import (...)

  • 以下のような代入

    • target = (...)

    • target: type = (...)

    • some, *un, packing = (...)

    • augmented += (...)

これらの場合、ステートメント全体が1行に収まる場合、または内側の式にさらに分割するための区切り文字がない場合、括弧は削除されます。区切り文字が1つだけで、式が括弧で始まるか終わる場合、既存の括弧ペアが式をきれいに整理するため、括弧を省略することもできます。それ以外の場合、括弧が追加されます。

Blackは、明確さやさらなるコード編成のために追加したい可能性のある追加のネストされた括弧を追加または削除しないことに注意してください。たとえば、これらの括弧は削除されません。

return not (this or that)
decision = (maybe.this() and values > 0) or (maybe.that() and values < 0)

メソッドチェーン

ORMのような一部の一般的なAPIは、メソッドチェーンを使用します。このAPIスタイルはfluentインターフェースとして知られています。Blackは、呼び出しまたはインデックス操作に続くドットを、非常に優先度の低い区切り文字として扱うことで、それらをフォーマットします。説明するよりも動作を示す方が簡単です。例を見てください。

def example(session):
    result = (
        session.query(models.Customer.id)
        .filter(
            models.Customer.account_id == account_id,
            models.Customer.email == email_address,
        )
        .order_by(models.Customer.id.asc())
        .all()
    )

型スタブファイル

PEP 484は、Pythonの型ヒントの構文を記述しています。型付けのユースケースの1つは、直接型アノテーションを含めることができないモジュールに型アノテーションを提供することです(それらはCで記述されているか、サードパーティ製であるか、またはその実装が過度に動的である可能性があります)。

これを解決するために、外部モジュールの型情報を記述するために、.pyiファイル拡張子を持つスタブファイルを使用できます。これらのスタブファイルは、記述するクラスと関数の実装を省略し、代わりにファイルの構造(グローバル変数、関数、およびメンバーを持つクラスのリスト)のみを含みます。これらのファイルの推奨されるコードスタイルは、PEP 8よりも簡潔です。

  • クラス/関数のシグネチャと同じ行に...を優先します。

  • 連続するモジュールレベルの関数、名前、または1つのクラス内のメソッドとフィールドの間に垂直方向の空白を避けます。

  • トップレベルのクラス定義の間には1つの空白行を使用するか、クラスが非常に小さい場合は空白行を使用しないでください。

Blackは上記のルールを適用します。まだ適用されていませんが、将来のバージョンのフォーマッターで適用される可能性がある.pyiファイルをフォーマットするための追加のガイドラインがあります。

  • passよりも...を優先します。

  • 型アノテーションで文字列リテラルを使用することを避けます。スタブファイルは前方参照をネイティブにサポートしています(from __future__ import annotationsを使用したPython 3.7コードのように)。

  • 古いバージョンのPythonをターゲットとするスタブの場合でも、型コメントの代わりに変数アノテーションを使用します。

改行

Blackは、ファイルの最初の改行に基づいて、改行(\nまたは\r\n)を正規化します。

改ページ文字

Blackは、モジュールレベルで、他の場合は空の行にある改ページ文字を保持します。連続する空の行のグループに対しては、1つの改ページのみが保持されます。連続して2つの空の行がある場合、改ページは2行目に配置されます。

実用主義

Blackの初期バージョンは、いくつかの点で絶対主義的でした。それはその最初の作成者の後を追いました。当時、実装がより簡単になり、ユーザーもあまりいなかったので、これは問題ありませんでした。報告されたエッジケースは多くありませんでした。成熟したツールとして、Blackはそうでない場合に保持するルールにいくつかの例外を設けます。このセクションでは、これらの例外とその理由について説明します。

マジックトレーリングカンマ

一般に、Blackは既存のフォーマットを考慮しません。

ただし、コードに短いコレクションまたは関数呼び出しを配置したが、将来的にそれが成長することを予期している場合があります。

たとえば

TRANSLATIONS = {
    "en_us": "English (US)",
    "pl_pl": "polski",
}

Blackの初期バージョンでは、それらを容赦なく1行に折りたたんでいました(収まります!)。今では、コレクションに末尾のカンマを自分で入れることで、それを望まないことを伝えることができます。そうすると、Blackは常にコレクションを1行に1つの項目に展開することを知ります。

どのようにしてそれを停止させるのですか?その末尾のカンマを削除するだけで、収まる場合はBlackがコレクションを1行に折りたたみます。

必要に応じて、オプション--skip-magic-trailing-comma / -Cを使用して、Blackの初期バージョンの動作を回復できます。

r”文字列”とR”文字列”

Blackは、文字列の接頭辞と同様に文字列の引用符を正規化し、小文字にします。このルールの1つの例外はr-文字列です。GitHubやVisual Studio Codeなどでデフォルトで使用されている非常に人気のあるMagicPython構文ハイライターは、r-文字列とR-文字列を区別することが判明しました。前者は正規表現として構文強調表示されますが、後者は特別なセマンティクスを持たない真のraw文字列として扱われます。

フォーマット前後のAST

--safe(デフォルト)で実行する場合、Blackはフォーマット前後のコードが意味的に同等であることを確認します。このチェックは、ソースのASTとターゲットのASTを比較することによって行われます。ASTが異なる限定的なケースは3つあります。

  1. Black は、ドキュメントストリングの先頭と末尾の空白を削除し、必要に応じてインデントを調整します。これは、フォーマッターがドキュメントストリングの空白問題を修正する最も人気のあるユーザー報告機能の1つです。結果は技術的にはASTの差異ですが、ドキュメントストリングを形成するさまざまな可能性により、私たちが知っているドキュメントストリングのすべての実際の使用例では、いずれにしてもインデントと先頭/末尾の空白がサニタイズされます。

  2. Black は、一部のステートメントでオプションの括弧を管理します。del ステートメントの場合、括弧で囲むかどうかの有無は、結果のASTを変更しますが、インタプリタでは意味的に同等です。

  3. Black は、型コメントを含むコメントを移動する可能性があります。これらは、Python 3.8以降、ASTの一部です。ツールはこれらのコメントに対して多数の特別なケースを実装していますが、それらがソース内の元の場所にとどまる保証はありません。これにより、ソースコードのランタイム動作が変更されることはありません。

状況を説明すると、コードの同等性チェックは、他のフォーマッターではまったく実装されていないBlackの機能です。コードがフォーマットされる前の状態と同じように動作することを保証することは、私たちにとって非常に重要です。これを機能として扱い、将来緩和する計画はありません。上記の例外は、ユーザーからのフィードバックまたはツールの実装詳細のいずれかに起因します。いずれの場合も、ASTの差異が実質的に影響がないことを保証するために、十分な注意を払いました。