こんにちは、かつコーチです。
今回は、Pythonらしいコードを書くうえで欠かせないリスト内包表記を解説します。
リスト内包表記とは、for文を使ったリストの生成処理を、1行で簡潔に書ける構文のことです。
他の人が書いたPythonコードを読んでいると、[x * 2 for x in numbers]のような書き方を目にすることが多いのではないでしょうか。
私も初めてこの構文を見たとき、「forとif、それに計算式が1行に詰め込まれていて読み方が分からない」と感じました。
この記事では、通常のfor文との比較からスタートし、リスト内包表記の基本構文、条件付きの書き方まで、順を追って解説します。
リスト内包表記とは?通常のfor文との比較
通常のfor文でリストを作る場合
まず、通常のfor文を使って、数値のリストを2倍にした新しいリストを作る処理を見てみます。
numbers = [1, 2, 3, 4, 5]
doubled = []
for n in numbers:
doubled.append(n * 2)
print(doubled) # [2, 4, 6, 8, 10]
「空のリストを用意する」「for文で1つずつ取り出す」「appendで追加する」という3つのステップが必要です。
このパターンは、Pythonのコードの中で非常によく登場します。
リスト内包表記で書き直す
同じ処理を、リスト内包表記で書くと次のようになります。
numbers = [1, 2, 3, 4, 5]
doubled = [n * 2 for n in numbers]
print(doubled) # [2, 4, 6, 8, 10]
たった1行で、先ほどの4行分の処理と同じ結果が得られました。
リスト内包表記の基本構文は、次の形になります。
[式 for 変数 in イテラブル]
イテラブルとは、リストや文字列、範囲(range)など、for文で1つずつ取り出せるデータのことです。
「イテラブルの中身を1つずつ変数として取り出し、式で加工した結果を新しいリストにする」という流れは、通常のfor文と同じです。
なぜリスト内包表記を使うのか
リスト内包表記を使う主なメリットは、次の2点です。
- コードが短くなり、可読性が上がる(慣れれば「何をしているか」が1行で把握できる)
- 処理速度がやや速い(
appendを繰り返すより効率的に処理される)
一方で、内容が複雑になりすぎると、かえって読みにくくなるというデメリットもあります。
このバランス感覚については、後半の「よくあるつまずきポイント」で詳しく解説します。
条件付きリスト内包表記
if文を使ったフィルタリング
リスト内包表記の中には、条件(if)を追加することもできます。
numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
even_numbers = [n for n in numbers if n % 2 == 0]
print(even_numbers) # [2, 4, 6, 8, 10]
構文は次の形になります。
[式 for 変数 in イテラブル if 条件]
「イテラブルの中から、条件に合うものだけを取り出して式で加工する」という意味です。
if n % 2 == 0という条件によって、偶数だけがリストに含まれるようになっています。
if…elseを使った値の切り替え
条件によって「含める・含めない」だけでなく、「値そのものを切り替える」場合は、前回解説した三項演算子と組み合わせます。
numbers = [1, 2, 3, 4, 5]
labels = ["偶数" if n % 2 == 0 else "奇数" for n in numbers]
print(labels) # ['奇数', '偶数', '奇数', '偶数', '奇数']
この場合、構文の形が少し変わる点に注意してください。
[値A if 条件 else 値B for 変数 in イテラブル]
先ほどのフィルタリング用のifとは違い、このif...elseはforより前に置かれています。
「forの後ろにif単体があればフィルタリング」「forの前にif…elseがあれば値の切り替え」という違いを意識すると、混同しにくくなります。
よくあるつまずきポイント・エラー対処
つまずき1:if単体とif…elseの位置を混同する
初心者が最初につまずきやすいのが、まさに先ほど触れたifの位置です。
❌ Before:if…elseをforの後ろに書いてSyntaxErrorになる
numbers = [1, 2, 3, 4, 5]
labels = [n for n in numbers if n % 2 == 0 else "奇数"]
SyntaxError: invalid syntax
私も最初にこのエラーに遭遇したとき、「if…elseの三項演算子と同じ感覚で書いたはずなのに、なぜ構文エラーになるのか」と戸惑いました。
原因は、forの後ろに置けるifはフィルタリング用の単独ifであり、elseを伴う三項演算子はforより前に置く必要がある、というルールを知らなかったことです。
✅ After:値を切り替えたいならforの前にif…elseを書く
numbers = [1, 2, 3, 4, 5]
labels = ["偶数" if n % 2 == 0 else "奇数" for n in numbers]
print(labels) # ['奇数', '偶数', '奇数', '偶数', '奇数']
「除外したいだけならforの後ろにifだけ」「値を切り替えたいならforの前にif…else」という使い分けを、しっかり覚えておきましょう。
つまずき2:ネストが深すぎて読めなくなる
もう1つのよくあるつまずきは、内包表記を詰め込みすぎて可読性が下がるケースです。
❌ Before:1行に処理を詰め込みすぎている
matrix = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
result = [x * 2 for row in matrix for x in row if x % 2 == 0 if x > 2]
print(result) # [8, 12, 16]
このコードは動作しますが、forが2つとifが2つ連続していて、一読しただけでは処理内容を把握しづらくなっています。
私が実際にコードレビューでこの手のコードを見たときも、「動いているが理解に時間がかかる」というコメントを何度もしたことがあります。
✅ After:通常のfor文に分解するか、処理を分割する
matrix = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
result = []
for row in matrix:
for x in row:
if x % 2 == 0 and x > 2:
result.append(x * 2)
print(result) # [8, 12, 16]
内包表記が複雑になりすぎたと感じたら、無理に1行に収めず、通常のfor文に戻すという判断も大切です。
「内包表記は、1つのforと1つのifまでに収めると読みやすい」というのが、私が実務で意識している目安です。
それ以上複雑になる場合は、処理を関数に切り出すなど、別のアプローチを検討しましょう。
応用・一歩先の使い方
ジェネレータ式との違い
リスト内包表記とよく似た構文に、ジェネレータ式があります。
numbers = [1, 2, 3, 4, 5]
# リスト内包表記(角括弧)
list_comp = [n * 2 for n in numbers]
print(list_comp) # [2, 4, 6, 8, 10]
# ジェネレータ式(丸括弧)
gen_exp = (n * 2 for n in numbers)
print(gen_exp) # <generator object <genexpr> at 0x...>
print(sum(gen_exp)) # 30
角括弧[]を丸括弧()に変えるだけで、ジェネレータ式になります。
リスト内包表記はメモリ上に全要素を持つリストを作りますが、ジェネレータ式は要素を1つずつ必要なタイミングで生成するイテレータを作ります。
大量のデータを扱う場合、ジェネレータ式の方がメモリ効率がよくなることがあります。
sum()やmax()のように、要素を1つずつ処理する関数と組み合わせる場合は、ジェネレータ式を使う選択肢も検討する価値があります。
関数と組み合わせる
リスト内包表記の式の部分には、自作関数を含めることもできます。
def to_full_width_yen(price):
return f"{price:,}円"
prices = [1000, 25000, 380000]
formatted = [to_full_width_yen(p) for p in prices]
print(formatted) # ['1,000円', '25,000円', '380,000円']
このように、加工処理を関数として切り出しておくと、リスト内包表記自体はシンプルなまま保てます。
処理が複雑になりそうなときは、「内包表記の中身を全部書く」のではなく、「関数を呼び出すだけにする」という方法も覚えておくと便利です。
まとめ
この記事のポイント
- リスト内包表記は
[式 for 変数 in イテラブル]という構文で、for文とappendの処理を1行にまとめられる - フィルタリングしたいときは
[式 for 変数 in イテラブル if 条件]のようにforの後ろにifを置く - 値を切り替えたいときは
[値A if 条件 else 値B for 変数 in イテラブル]のようにforの前にif…elseを置く - forとifを詰め込みすぎると読みにくくなるため、複雑な場合は通常のfor文に戻す判断も大切
- 丸括弧に変えるとジェネレータ式になり、大量データを扱う場合のメモリ効率がよくなる
次に読むべき記事
リスト内包表記が使えるようになったら、次は辞書やセットの内包表記も押さえておきましょう。
→ 次の記事:Pythonの辞書内包表記・セット内包表記の書き方
タグ: Python, 中級者向け, 基本文法