リスト内包表記とは?
リスト内包表記(List Comprehension)は、Pythonでリストを簡潔に作成するための構文です。forループで1つずつappendする代わりに、1行で読みやすく、しかも高速に書けます。
従来のfor文と比べてみましょう:
# for文で書いた場合(4行)
squares = []
for x in range(10):
squares.append(x ** 2)
# リスト内包表記(1行)
squares = [x ** 2 for x in range(10)]
# どちらも結果は同じ
# [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
💡 リスト内包表記はPython愛好家の間で「Pythonic(Pythonらしい)」と評される代表的な書き方です。読みやすさだけでなく、内部最適化により普通のforループより20〜50%速いのが魅力です。
基本構文
[式 for 変数 in イテラブル]
もっとも単純な形は、既存のリストから新しいリストを作るパターンです:
# 各要素を2倍にした新しいリスト
nums = [1, 2, 3, 4, 5]
doubled = [n * 2 for n in nums]
# [2, 4, 6, 8, 10]
# 文字列のリストを大文字に
words = ["apple", "banana", "cherry"]
upper_words = [w.upper() for w in words]
# ['APPLE', 'BANANA', 'CHERRY']
# 文字列の各文字をリストに
chars = [c for c in "Python"]
# ['P', 'y', 't', 'h', 'o', 'n']
条件付きフィルタ(if)
if句を末尾に書くと、条件に合う要素だけを抽出できます。
# 偶数だけを抽出
nums = range(10)
evens = [n for n in nums if n % 2 == 0]
# [0, 2, 4, 6, 8]
# 5文字以上の単語だけ
words = ["dog", "elephant", "cat", "tiger"]
long_words = [w for w in words if len(w) >= 5]
# ['elephant', 'tiger']
# 条件は複数つなげられる
result = [n for n in range(100) if n % 3 == 0 if n % 5 == 0]
# [0, 15, 30, 45, 60, 75, 90]
三項演算子(if-else)を使う
「条件によって変換する値を変える」場合、if-else を式の側に書きます。位置に注意:
# 構文:[式 if 条件 else 別の式 for 変数 in イテラブル]
# 偶数なら2倍、奇数なら-1倍
nums = [1, 2, 3, 4, 5]
result = [n * 2 if n % 2 == 0 else -n for n in nums]
# [-1, 4, -3, 8, -5]
# 文字列の長さで分類
words = ["hi", "hello", "yo", "morning"]
labels = ["short" if len(w) < 4 else "long" for w in words]
# ['short', 'long', 'short', 'long']
⚠️
・フィルタ:
・変換:
ifだけ(フィルタ)とif-else(変換)では位置が違います。・フィルタ:
[x for x in items if 条件] ← 末尾・変換:
[x if 条件 else y for x in items] ← 先頭ネストしたループ
多重ループも内包表記で書けます。記述順は通常のforループと同じです。
# 九九の表をフラットなリストで生成
table = [i * j for i in range(1, 10) for j in range(1, 10)]
# [1, 2, 3, ..., 81]
# 2次元配列のフラット化
matrix = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
flat = [n for row in matrix for n in row]
# [1, 2, 3, 4, 5, 6, 7, 8, 9]
# ペアの組み合わせ
pairs = [(x, y) for x in [1, 2, 3] for y in ['a', 'b']]
# [(1,'a'),(1,'b'),(2,'a'),(2,'b'),(3,'a'),(3,'b')]
2次元リストの作り方
# 3×3の二次元リスト(内側のリストを内包で作る)
matrix = [[i * 3 + j for j in range(3)] for i in range(3)]
# [[0, 1, 2], [3, 4, 5], [6, 7, 8]]
辞書内包表記・集合内包表記
リストだけでなく、辞書と集合も同じ要領で作れます。
辞書内包表記
# {キー: 値 for ...}
squares_dict = {n: n ** 2 for n in range(5)}
# {0: 0, 1: 1, 2: 4, 3: 9, 4: 16}
# 既存辞書の変換(値を2倍に)
prices = {"apple": 100, "banana": 80, "cherry": 200}
discounted = {k: v * 0.8 for k, v in prices.items()}
# キーと値を入れ替え
swapped = {v: k for k, v in prices.items()}
# {100: 'apple', 80: 'banana', 200: 'cherry'}
# 条件付き
expensive = {k: v for k, v in prices.items() if v >= 100}
集合内包表記
# {式 for ...}
unique_lengths = {len(w) for w in ["apple", "banana", "fig", "kiwi"]}
# {3, 4, 5, 6}
ジェネレータ式(()を使う)
角カッコ [] ではなく丸カッコ () を使うと、メモリを節約できるジェネレータ式になります。
# リスト内包表記:すぐに全要素をメモリに展開
big_list = [n ** 2 for n in range(10_000_000)] # メモリ大量消費
# ジェネレータ式:必要なときに1つずつ生成
big_gen = (n ** 2 for n in range(10_000_000)) # ほぼメモリ消費なし
# sum() などはジェネレータと相性◎
total = sum(n ** 2 for n in range(1000))
💡 大量データを処理する場合や、合計・最大値だけ欲しい場合はジェネレータ式が断然有利です。リスト全体を保持する必要がないため省メモリになります。
パフォーマンス比較
10万要素の二乗を計算する処理時間の例(ざっくり):
| 書き方 | 相対速度 |
|---|---|
| for文 + append | 1.0倍(基準) |
| リスト内包表記 | 約 1.3〜1.5倍速い |
| map + lambda | 内包表記とほぼ同等 |
速度差は実装によりますが、内包表記が遅くなることはほぼありません。
実用例
1. ファイル名から拡張子を抽出
files = ["data.csv", "report.pdf", "image.png"]
exts = [f.split(".")[-1] for f in files]
# ['csv', 'pdf', 'png']
2. 文字列を数値に変換
raw = ["1", "2", "3", "abc", "4"]
nums = [int(s) for s in raw if s.isdigit()]
# [1, 2, 3, 4]
3. CSVデータの整形
rows = ["1,apple,100", "2,banana,80", "3,cherry,200"]
parsed = [
{"id": int(r.split(",")[0]),
"name": r.split(",")[1],
"price": int(r.split(",")[2])}
for r in rows
]
4. 行列の転置
matrix = [[1, 2, 3], [4, 5, 6]]
transposed = [[row[i] for row in matrix] for i in range(len(matrix[0]))]
# [[1, 4], [2, 5], [3, 6]]
使うべきでない場面
万能ではありません。以下の場合は素直にforループを使うほうが読みやすいです。
- 3重以上のネスト:可読性が落ちる
- 各反復で副作用が必要(print、ファイル書き込みなど)
- 条件分岐が複雑:if-elif-elseが必要なケース
- 例外処理が必要:try/exceptは内包表記の中に書けない
⚠️ 「短く書けるから」だけで内包表記に詰め込みすぎると、読み手にとって解読パズルになります。3行のforループのほうが読みやすいなら、その方が良いコードです。
まとめ
- リスト内包表記はシンプル・高速・Pythonic
- 基本:
[式 for 変数 in イテラブル] - フィルタ:末尾に
if 条件 - 変換時の条件分岐:先頭に
x if 条件 else y - 辞書・集合・ジェネレータ式も同じ感覚で書ける
- 3重以上のネストや副作用がある処理ではforループの方が読みやすい
リスト内包表記を使いこなせると、Pythonコードが圧倒的に簡潔になります。日々のコードレビューで「ここは内包表記で書けるな」と気づけるようになれば、Python中級者の仲間入りです!