コンテンツ

Python速習ガイド - ステップ2 制御フロー (5) - ループ(3): 内包表記

Info
  • 本講座は、Pythonプログラミングの基礎を手を動かしながら最速で身につけるための講座です。
  • 「スタイルガイド」では、Pythonできれいなコードを書くためのガイドライン(PEP8)で紹介されている内容を主に記載しています。
  • 各コードは実行して結果を確認することができます。
    ページの再読み込みで元の内容に戻りますので、自由にいじってみてください。

「ステップ2 制御フロー」の続きです。

内包表記(Comprehension) は、イテラブルなオブジェクトを簡単に作成するための構文です。

  • 従来のループ処理を1行で表現できるため、コードが簡潔になります
  • for文を使用した生成よりも一般的に高速に動作します

リスト内包表記(list comprehension)は、ループを使ってリストを生成する簡潔な構文です。

リスト内包表記
[要素を使った式 for 要素 in イテラブル]

上記のリスト内包表記は、以下のfor文と同じリストを生成します:

内包表記は以下のように条件を追加することも可能です

リスト内包表記 (条件付き)
[要素を使った式 for 要素 in イテラブル if 要素の条件]

上記リストをfor文を用いて作成するコードは以下のとおりです:

リスト内包表記は多重ループにも対応しています

リスト内包表記 (多重ループ)
[ for 要素 in イテラブル for 要素2 in イテラブル2 ...]

下記for文と同じリストを生成します:

多重ループの記述順序

多重ループの場合、forの記述順は通常のネストされたfor文と同じ順序で書く必要があります。

上の内包表記は、次のfor文と同じ意味になります:

このように、外側のループ(for student in students_scores)を先に書き、内側のループ(for score in student)を後に書く必要があります。順序を逆にして for score in student for student in students_scores のように書くと、student が未定義の状態で使われるためエラーになります。

スタイルガイド
  • 内包表記の記述が長くなる場合は複数行に分割しましょう
  • 複雑すぎる内包表記は可読性が低下するため、従来のループの使用を検討しましょう
📚練習問題

1 * 1 + 2 * 2 + ... + 100 * 100 の計算結果をリスト内包表記を使用して求めましょう。

解答例
📚練習問題

1から20までの数のうち、3の倍数または5の倍数のリストをリスト内包表記で作成しましょう。

解答例
📚練習問題

あるホテルの各階の空き部屋のリストは以下のとおりです。

vacant_rooms_by_floor = [
    [101, 102, 103],  # 1階の空き部屋
    [202, 203],       # 2階の空き部屋
    [301, 303, 305]   # 3階の空き部屋
]
  1. すべての空き部屋からなるリストを表示しましょう
  2. 末尾が1または5の部屋は角部屋です。空き部屋の内、角部屋のみのリストを表示しましょう
    (ヒント: 末尾の数字は <部屋番号> % 10 で計算できます)
解答例
コラム: リスト内包表記と通常ループの速度比較

ベンチマークツールを用いて、リスト内包表記と通常ループの処理速度を比較してみます:

ベンチマーク結果より、ループより内包表記のほうが速いことがわかります。

辞書内包表記(dict comprehension)は、ループを使って簡潔に辞書を生成する構文です。

辞書内包表記
{キーの式: 値の式 for 要素 in イテラブル if 条件}

辞書内包表記を使って既存の辞書を変換することもできます:

📚練習問題

学生の得点を記録した辞書があります。

  1. 合格(60点以上)した学生だけの辞書を作成しましょう
  2. 合格した各学生に対して、80点以上の場合は「優」それ以外は「良」という評価を対応させる辞書を作成しましょう (ヒント: 三項演算子を利用しましょう)
解答例

集合内包表記(set comprehension)は、ループを使って簡潔に集合を生成する構文です。

集合内包表記
{ for 要素 in イテラブル if 条件}

ジェネレータ式は、内包表記に似ていますが、結果を一度に生成するのではなく、必要に応じて一つずつ値を生成するオブジェクト(ジェネレータ)を返します。

  • メモリ効率が良いため、大きなデータセットを扱う際に便利です。
  • ただし、1度使用すると空になって使えなくなることに注意してください。
ジェネレータ式
( for 要素 in イテラブル if 条件)
📚練習問題

リスト内包表記の練習問題で扱った 1 * 1 + 2 * 2 + ... + 100 * 100 の計算を、
よりメモリ効率の良いジェネレータ式を用いて実行しましょう。

解答例
コラム:リスト内包表記とジェネレータ式のメモリ効率を実際に比較

ベンチマークツールを用いて、実際にリスト内包表記とジェネレータ式のメモリ効率を比較してみます。

上記ベンチマーク結果より、ジェネレータ式の場合はサイズによらずメモリ使用量が少なく、
リスト内包表記の場合はサイズに比例してメモリ使用量が増加することがわかります。

まとめ
種類 構文 用途 主な特徴
リスト内包表記 [式 for 要素 in イテラブル if 条件] リスト作成 リストを簡潔に生成
辞書内包表記 {キー: 値 for 要素 in イテラブル if 条件} 辞書作成 辞書を簡潔に生成
集合内包表記 {式 for 要素 in イテラブル if 条件} 集合作成 集合を簡潔に生成
ジェネレータ式 (式 for 要素 in イテラブル if 条件) 遅延評価 メモリ効率がよい。一度限りの使用
  • リスト内包表記は通常のループよりも一般的に高速に動作する
  • 複雑すぎる条件式は可読性が低下するため、従来のループの使用を検討する
  • 大量のデータを扱う場合は、ジェネレータ式を使うとメモリ使用量を抑えることができる

関連記事