2.30. 内包表記

内包表記は、既存のイテラブルから新しいリスト、セット、辞書、またはジェネレータを1つの式で構築します。空のコンテナから始めてループ内で追加していくという一般的なパターンの代替です。

2.30.1. リスト内包表記

squares = [x * x for x in range(5)]
print(squares)

出力:

[0, 1, 4, 9, 16]

同じループを展開して書くと:

squares = []
for x in range(5):
    squares.append(x * x)

内包表記の形式は、その場でリストを構築する1つの式です。squares = [].append もありません。結果は内包表記の値そのものです。

式 "[f(x) for x in xs if cond]" に注釈を付けたもの: 先頭の式が結果、for句はループ変数に 名前を付け、if句はどの項目を 残すかをフィルタする。

先頭の式が各項目を生成し、for 句がループ変数に名前を付け、省略可能な if が項目をフィルタで除外します。

2.30.2. ifによるフィルタリング

省略可能な if 句は、一致する項目だけを残します:

evens = [x for x in range(10) if x % 2 == 0]
print(evens)

出力:

[0, 2, 4, 6, 8]

フィルタは先頭の式より先に実行されます。x % 2 == 0 が最初にチェックされ、一致する値だけが出力のために x に到達します。

2.30.3. 辞書内包表記とセット内包表記

同じ形が辞書リテラルやセットリテラルでも機能します。

辞書内包表記は、for の前に key: value のペアを持ちます:

squares = {x: x * x for x in range(5)}
print(squares)

出力:

{0: 0, 1: 1, 2: 4, 3: 9, 4: 16}

セット内包表記は波括弧と単一の式を使います:

unique_lengths = {len(w) for w in ["a", "bb", "c", "bb"]}
print(unique_lengths)

出力:

{1, 2}

2.30.4. ジェネレータ式

丸括弧はリストの代わりにジェネレータ式を生成します。値は必要に応じて1つずつ計算されます:

total = sum(x * x for x in range(1000))

100万要素のリストが構築されることは決してありません。値は1つずつ sum() に流れ込み、加算されながら各値が順に破棄されていきます。

ジェネレータ式は、値を縮約関数(sum()max()any()all())やその他のイテレータを消費するコードに渡すときの正しい選択です。同等のリストが使うはずだったメモリを節約できます。

2.30.5. 内包表記を使うべきでないとき

内包表記は簡潔ですが、必ずしもより分かりやすいとは限りません。次のような場合は素直な for ループを使いましょう:

  • 本体に複数の文が必要な場合(内包表記にちょうど1つの式しか収まりません)。

  • 本体に副作用がある場合(出力やファイルへの書き込みなど)。内包表記はコレクションを構築するためのものであり、アクションを実行するためのものではありません。

  • フィルタや変換の構成要素が多すぎて、内包表記がもはや左から右へ読めなくなる場合。