リスト内包表記でループを 1 行に

生田 陸人
LuaGate エンジニア / 現役エンジニア
編集 LuaGate編集部

このレッスンで分かること

  • [式 for 変数 in iterable] の 1 行で for + append を置き換えられます
  • if を後ろに付けるとフィルタ、前に書くと三項演算子で値の切り替えになります
  • 最小例は [x * x for x in range(5)][0, 1, 4, 9, 16]

リスト内包表記でループを 1 行に とは

Python のリスト内包表記で for ループを短く美しく書く方法を学びます。本レッスンでは、リスト内包表記でループを 1 行に の基本から実際の使いどころまでを整理し、現場で迷わず使える形に落とし込みます。

リスト内包表記でループを 1 行に

Python でリストを加工するとき、for ループと append を毎回書くのは少し冗長です。そんなとき リスト内包表記 を使えば、[式 for 変数 in iterable] の 1 行で同じことができます。読みやすく、書く量も減り、しかも内部的には少し速い、それが内包表記の魅力です。Python らしいスタイルとして PEP 8 でも標準的に推奨される書き方の一つです。

リスト内包表記は「これがやりたい」という結果を中心に書ける構文です。for ループは手続きを書く、内包表記は 結果のかたち を書く、と視点が少し違います。

リスト内包表記とは何か

基本形は [expression for item in iterable] です。さらに if を付ければフィルタリング、ネストすれば二次元の組み合わせも作れます。for append をセットで書く昔ながらのコードを置き換える、現代 Python の中心的な構文です。

Python

# 普通の for ループで 2 乗のリスト squares = [] for x in range(5): squares.append(x * x) print(squares) # [0, 1, 4, 9, 16] # リスト内包表記で書き換え squares = [x * x for x in range(5)] print(squares) # [0, 1, 4, 9, 16]

5 行が 1 行になりました。for x in range(5)x を式 x * x に入れて並べる、というイメージです。mapfilter の組み合わせでも同じことはできますが、内包表記の方が Python では好まれます。

文法のおさらい

リスト内包表記には次のパターンがあります。

パターン意味
基本[x * 2 for x in nums]全要素を 2 倍
if 付き[x for x in nums if x > 0]条件で絞り込み
三項演算子[x if x > 0 else 0 for x in nums]条件で値を切り替え
ネスト[(a, b) for a in xs for b in ys]全組み合わせ

if後ろ に書くとフィルタ、 に書くと三項演算子で値の切り替え。位置で意味が変わるので注意しましょう。

Python

nums = [-3, -1, 0, 2, 5] positives = [n for n in nums if n > 0] print(positives) # [2, 5] clamped = [n if n > 0 else 0 for n in nums] print(clamped) # [0, 0, 0, 2, 5]

動きを追ってみる

diagram (will load when visible)

図の流れを順序リストで書き直すと、次の通りです。

  1. iterable から要素を 1 つ取り出す
  2. 後ろの if 条件があれば評価する
  3. 条件が真 (または if 無し) なら式を評価する
  4. 評価結果を新しいリストに追加する
  5. 次の要素に進み、iterable が尽きるまで繰り返す

概念図のように、for ループの 1 ステップを式と if 句で表現し、結果を 新しいリスト に集めるイメージです。元の iterable には触れません。ジェネレータ式 (x for x in nums) という親戚もあって、メモリを節約したいときはこちらを使います。

よくある間違い

  1. if の位置を間違える。「条件で値を切り替え」は [value_a if cond else value_b for x in iterable]、「フィルタ」は [value for x in iterable if cond]。同じ if でも役割が違います
  2. 内包表記の中で副作用を書いてしまう。printappend を中で呼ぶのは避け、純粋な「式」だけを書きましょう
  3. ネストが深くなり読みづらくなる。3 つ以上の for が並ぶ場合は、素直に for ループに戻すほうが親切です

内包表記は便利ですが、読み手のことを忘れないことが大切です。計算ロジックが複雑になりそうなら、関数に切り出して名前を付ける方が良い設計です。

内包表記 vs map / filter

mapfilter でも同じ処理は書けますが、内包表記は 読みやすさ で勝ります。下のコードは同じ結果になります。

Python

nums = [1, 2, 3, 4] squared_map = list(map(lambda x: x * x, nums)) squared_compr = [x * x for x in nums]

lambda のキーワードを書かずに済む分、内包表記の方が短く意図が伝わります。Python のコミュニティでは「Pythonic な書き方」として内包表記が好まれます。map filter はジェネレータを返すので、巨大データのストリーミング処理で使い分けるのが定石です。

この章のポイント

ここまでの要点 [式 for x in iterable] が基本形。フィルタは末尾 if、値の切り替えは先頭の三項演算子。副作用を持たない純粋な式だけを書く。

やってみよう

整数リスト nums を受け取り、各要素を 2 乗した新しいリストを返す関数 squared_list をリスト内包表記で書いてみましょう。for append を使わずに 1 行で書ければ完璧です。x * x でも x ** 2 でも結果は同じになります。空リストでも空リストを返すこと、これだけ覚えておけば十分です。

よくある質問

Q. for ループと内包表記はどっちが速いですか?

A. 一般に内包表記の方が高速です。CPython の最適化が効くため、append でリストを伸ばすより [f(x) for x in items] の方が 1.5〜2 倍程度速くなることが多いです。ただし読みにくくなるなら for ループに戻すべきで、可読性が最優先です。

Q. if を含む内包表記の書き方は?

A. [x2 for x in nums if x > 0] のように for の後に if を置きます。else も含めたい場合は [x2 if x > 0 else 0 for x in nums] のように三項演算子を前に置く形になります。順序が違うので混同しないよう注意してください。

Q. 辞書や set にも内包表記はありますか?

A. {k: v for k, v in pairs} で辞書内包、{x for x in items if cond} で set 内包になります。集合演算(重複排除、共通要素)を 1 行で書けるので便利ですが、複数の for を入れ子にすると読みにくくなるため 2 段までに留めるのが目安です。

次のレッスン

次は 辞書の基本でキーから値を取り出す で、Python のリスト内包表記で for ループを短く美しく書く方法を学びます を学びます。

事前確認 — 進む前に次の 3 つができることを確認しましょう。

  1. リスト内包表記 の要点を自分の言葉で説明できる
  2. このレッスンの最小コード (または操作手順) を見ずに書ける
  3. 練習問題やクイズで間違えた箇所を読み直して理解した

理解度チェック (30 秒)

Q. リスト内包表記 とは何か、1 文で説明してください。

この章のポイント

A. 本文の「このレッスンで分かること」または冒頭の説明文を見直し、自分の言葉で要約できれば OK。詰まったら本レッスンの最初の H2 セクションを読み返してみましょう。

関連レッスン

要件

  1. リスト内包表記 [expression for x in nums] を使う
  2. 元のリストを破壊しない
  3. 各要素を 2 乗した値を新しいリストとして返す

入出力例

test-cases.txt

squared_list([1,2,3,4])[1,4,9,16] squared_list([-2,-1,0,1,2])[4,1,0,1,4] squared_list([7])[49] squared_list([])[]

ヒント

main.py
main.py
学習モード

メモ

リスト内包表記でループを 1 行に

⌘S で保存