内包表記とは
Pythonの内包表記(comprehension)は、イテラブルから新しいコレクションを簡潔に生成する構文。
これを使いこなすと、コードがものすごくスッキリする。
# forループ
squares = []
for x in range(10):
squares.append(x**2)
# 内包表記(1行!)
squares = [x**2 for x in range(10)]
1. リスト内包表記
基本形
[式 for 変数 in イテラブル]
squares = [x**2 for x in range(10)]
# [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
条件付き(フィルタ)
[式 for 変数 in イテラブル if 条件]
evens = [x for x in range(20) if x % 2 == 0]
# [0, 2, 4, 6, 8, 10, 12, 14, 16, 18]
if-else(三項演算子)
labels = ["偶数" if x % 2 == 0 else "奇数" for x in range(5)]
# ['偶数', '奇数', '偶数', '奇数', '偶数']
ネストしたループ
# 2重ループ
coords = [(i, j) for i in range(3) for j in range(3)]
# [(0, 0), (0, 1), (0, 2), (1, 0), (1, 1), (1, 2), (2, 0), (2, 1), (2, 2)]
# フラット化
nested = [[1, 2], [3, 4], [5, 6]]
flat = [x for row in nested for x in row]
# [1, 2, 3, 4, 5, 6]
2. 辞書内包表記
{キー: 値 for 変数 in イテラブル}
# 基本
square_dict = {x: x**2 for x in range(5)}
# {0: 0, 1: 1, 2: 4, 3: 9, 4: 16}
# リストから辞書
names = ["alice", "bob", "charlie"]
name_lengths = {name: len(name) for name in names}
# {'alice': 5, 'bob': 3, 'charlie': 7}
# キーと値を入れ替え
original = {"a": 1, "b": 2, "c": 3}
swapped = {v: k for k, v in original.items()}
# {1: 'a', 2: 'b', 3: 'c'}
# 条件付き
scores = {"Alice": 85, "Bob": 60, "Charlie": 90}
passed = {name: score for name, score in scores.items() if score >= 70}
# {'Alice': 85, 'Charlie': 90}
3. 集合内包表記
{式 for 変数 in イテラブル}
# ユニークな値のみ
unique_squares = {x**2 for x in range(-5, 6)}
# {0, 1, 4, 9, 16, 25} ← -5²と5²は両方25で1つ
# 文字列から
text = "hello world"
unique_chars = {c for c in text if c.isalpha()}
# {'h', 'e', 'l', 'o', 'w', 'r', 'd'}
4. ジェネレータ式
(式 for 変数 in イテラブル)
メモリ効率が良い(遅延評価):
gen = (x**2 for x in range(10))
# <generator object <genexpr> at 0x...>
# 必要な時に値を生成
print(list(gen)) # [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
組み込み関数との組み合わせ
# sum, max, min
total = sum(x**2 for x in range(10)) # 285
max_val = max(len(s) for s in names) # 7
# any, all
numbers = [2, 4, 6, 8, 10]
all(x % 2 == 0 for x in numbers) # True(全て偶数)
any(x > 5 for x in numbers) # True(5より大きいのがある)
5. 実践的な例
ログのフィルタリング
log_lines = [
"2025-01-01 ERROR: Connection failed",
"2025-01-01 INFO: Started",
"2025-01-02 ERROR: Timeout",
]
errors = [line for line in log_lines if "ERROR" in line]
# ['2025-01-01 ERROR: Connection failed', '2025-01-02 ERROR: Timeout']
JSONデータの変換
users = [
{"name": "Alice", "age": 30, "active": True},
{"name": "Bob", "age": 25, "active": False},
{"name": "Charlie", "age": 35, "active": True},
]
active_users = [u["name"] for u in users if u["active"]]
# ['Alice', 'Charlie']
行列の転置
matrix = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
transposed = [[row[i] for row in matrix] for i in range(len(matrix[0]))]
# [[1, 4, 7], [2, 5, 8], [3, 6, 9]]
6. walrus演算子との組み合わせ(3.8+)
:=(セイウチ演算子)で計算結果を再利用:
import re
texts = ["abc123", "hello", "world456", "test"]
# マッチしたものだけを取得
matched = [m.group() for t in texts if (m := re.search(r'\d+', t))]
# ['123', '456']
7. パフォーマンス
内包表記: 0.0515秒
forループ: 0.0852秒
map: 0.0678秒
内包表記はforループより約1.7倍高速!
理由:
- ループのオーバーヘッドが少ない
-
appendメソッド呼び出しがない
8. アンチパターン
❌ 複雑すぎる内包表記
# 読みにくい!
result = [x.strip().lower() for x in data
if x and len(x.strip()) > 3 and x[0].isalpha()]
✓ 関数に分割
def is_valid(x):
x = x.strip()
return x and len(x) > 3 and x[0].isalpha()
def transform(x):
return x.strip().lower()
result = [transform(x) for x in data if is_valid(x)]
❌ 副作用のある内包表記
# 副作用は避ける
results = [process_and_save(x) for x in items] # ❌
✓ forループを使う
for x in items:
process_and_save(x) # ✓
まとめ
| 種類 | 構文 | 結果 |
|---|---|---|
| リスト | [x for x in iter] |
list |
| 辞書 | {k: v for k, v in iter} |
dict |
| 集合 | {x for x in iter} |
set |
| ジェネレータ | (x for x in iter) |
generator |
使い分け:
- 結果を複数回使う → リスト
- キーと値のペア → 辞書
- ユニークな値 → 集合
- メモリ節約 → ジェネレータ
内包表記を適切に使って、Pythonicなコードを書きましょう!