リスト内包表記でループを 1 行に
このレッスンで分かること
[式 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 に入れて並べる、というイメージです。map や filter の組み合わせでも同じことはできますが、内包表記の方が 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]動きを追ってみる
図の流れを順序リストで書き直すと、次の通りです。
iterableから要素を 1 つ取り出す- 後ろの
if条件があれば評価する - 条件が真 (または
if無し) なら式を評価する - 評価結果を新しいリストに追加する
- 次の要素に進み、
iterableが尽きるまで繰り返す
概念図のように、for ループの 1 ステップを式と if 句で表現し、結果を 新しいリスト に集めるイメージです。元の iterable には触れません。ジェネレータ式 (x for x in nums) という親戚もあって、メモリを節約したいときはこちらを使います。
よくある間違い
ifの位置を間違える。「条件で値を切り替え」は[value_a if cond else value_b for x in iterable]、「フィルタ」は[value for x in iterable if cond]。同じifでも役割が違います- 内包表記の中で副作用を書いてしまう。
printやappendを中で呼ぶのは避け、純粋な「式」だけを書きましょう - ネストが深くなり読みづらくなる。3 つ以上の
forが並ぶ場合は、素直にforループに戻すほうが親切です
内包表記は便利ですが、読み手のことを忘れないことが大切です。計算ロジックが複雑になりそうなら、関数に切り出して名前を付ける方が良い設計です。
内包表記 vs map / filter
map や filter でも同じ処理は書けますが、内包表記は 読みやすさ で勝ります。下のコードは同じ結果になります。
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 つができることを確認しましょう。
- リスト内包表記 の要点を自分の言葉で説明できる
- このレッスンの最小コード (または操作手順) を見ずに書ける
- 練習問題やクイズで間違えた箇所を読み直して理解した
理解度チェック (30 秒)
Q. リスト内包表記 とは何か、1 文で説明してください。
A. 本文の「このレッスンで分かること」または冒頭の説明文を見直し、自分の言葉で要約できれば OK。詰まったら本レッスンの最初の H2 セクションを読み返してみましょう。
関連レッスン
要件
- リスト内包表記
[expression for x in nums]を使う - 元のリストを破壊しない
- 各要素を 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([]) → []