はじめに
内包表記(Comprehension)はPythonの特徴的な構文で、コレクションを簡潔に作成できます。
リスト内包表記
基本形
# 従来のfor文
squares = []
for x in range(10):
squares.append(x ** 2)
# リスト内包表記
squares = [x ** 2 for x in range(10)]
print(squares) # [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
条件付き
# 偶数だけを二乗
even_squares = [x ** 2 for x in range(10) if x % 2 == 0]
print(even_squares) # [0, 4, 16, 36, 64]
# if-else(三項演算子)
labels = ["偶数" if x % 2 == 0 else "奇数" for x in range(5)]
print(labels) # ['偶数', '奇数', '偶数', '奇数', '偶数']
ネストしたループ
# 九九の表
multiplication = [f"{i}×{j}={i*j}" for i in range(1, 4) for j in range(1, 4)]
print(multiplication)
# ['1×1=1', '1×2=2', '1×3=3', '2×1=2', '2×2=4', '2×3=6', '3×1=3', '3×2=6', '3×3=9']
# 2次元リストをフラット化
matrix = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
flat = [num for row in matrix for num in row]
print(flat) # [1, 2, 3, 4, 5, 6, 7, 8, 9]
実践例
# 文字列のリストを処理
names = [" Alice ", "BOB", "charlie"]
cleaned = [name.strip().title() for name in names]
print(cleaned) # ['Alice', 'Bob', 'Charlie']
# ファイルパスのリストから拡張子がpyのものを抽出
files = ["main.py", "data.csv", "utils.py", "readme.md"]
python_files = [f for f in files if f.endswith(".py")]
print(python_files) # ['main.py', 'utils.py']
# 辞書のリストから特定のキーを抽出
users = [
{"name": "太郎", "age": 25},
{"name": "花子", "age": 22},
{"name": "次郎", "age": 30}
]
names = [user["name"] for user in users if user["age"] >= 25]
print(names) # ['太郎', '次郎']
辞書内包表記
# 基本形
squares = {x: x ** 2 for x in range(5)}
print(squares) # {0: 0, 1: 1, 2: 4, 3: 9, 4: 16}
# 条件付き
even_squares = {x: x ** 2 for x in range(10) if x % 2 == 0}
print(even_squares) # {0: 0, 2: 4, 4: 16, 6: 36, 8: 64}
# キーと値を入れ替え
original = {"a": 1, "b": 2, "c": 3}
inverted = {v: k for k, v in original.items()}
print(inverted) # {1: 'a', 2: 'b', 3: 'c'}
# 2つのリストから辞書を作成
keys = ["name", "age", "city"]
values = ["太郎", 25, "東京"]
person = {k: v for k, v in zip(keys, values)}
print(person) # {'name': '太郎', 'age': 25, 'city': '東京'}
実践例
# 文字の出現回数をカウント
text = "hello world"
char_count = {char: text.count(char) for char in set(text)}
print(char_count) # {'h': 1, 'e': 1, 'l': 3, 'o': 2, ' ': 1, 'w': 1, 'r': 1, 'd': 1}
# 環境変数をフィルタリング
import os
python_env = {k: v for k, v in os.environ.items() if "PYTHON" in k}
# JSONデータの整形
raw_data = {"Name": "Taro", "AGE": 25, "City": "Tokyo"}
normalized = {k.lower(): v for k, v in raw_data.items()}
print(normalized) # {'name': 'Taro', 'age': 25, 'city': 'Tokyo'}
集合内包表記
# 基本形
squares = {x ** 2 for x in range(10)}
print(squares) # {0, 1, 4, 9, 16, 25, 36, 49, 64, 81}
# 重複を除去
words = ["apple", "banana", "apple", "orange", "banana"]
unique_lengths = {len(word) for word in words}
print(unique_lengths) # {5, 6}
# 条件付き
even_squares = {x ** 2 for x in range(10) if x % 2 == 0}
print(even_squares) # {0, 64, 4, 36, 16}
ジェネレータ式
メモリ効率が良い遅延評価版です。
# リスト内包表記(全要素をメモリに保持)
squares_list = [x ** 2 for x in range(1000000)]
# ジェネレータ式(必要な時に生成)
squares_gen = (x ** 2 for x in range(1000000))
# メモリ使用量の違い
import sys
print(sys.getsizeof(squares_list)) # 約8MB
print(sys.getsizeof(squares_gen)) # 約200バイト
# ジェネレータの使用
for square in squares_gen:
if square > 100:
print(square)
break
# 関数に渡す場合、括弧を省略できる
total = sum(x ** 2 for x in range(100))
print(total) # 328350
ネストした内包表記
# 2次元リストの作成
matrix = [[j for j in range(3)] for i in range(3)]
print(matrix) # [[0, 1, 2], [0, 1, 2], [0, 1, 2]]
# 転置
matrix = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
transposed = [[row[i] for row in matrix] for i in range(3)]
print(transposed) # [[1, 4, 7], [2, 5, 8], [3, 6, 9]]
可読性とパフォーマンス
いつ内包表記を使うか
# 良い例:シンプルな変換
squares = [x ** 2 for x in range(10)]
filtered = [x for x in items if x > 0]
# 悪い例:複雑すぎる
# これは普通のfor文の方が読みやすい
result = [
process(x)
for x in items
if x > 0
if validate(x)
for y in get_related(x)
if y.active
]
# 良い例:for文で書き直す
result = []
for x in items:
if x > 0 and validate(x):
for y in get_related(x):
if y.active:
result.append(process(x))
パフォーマンス比較
import timeit
# for文
def using_for():
result = []
for x in range(1000):
result.append(x ** 2)
return result
# リスト内包表記
def using_comprehension():
return [x ** 2 for x in range(1000)]
# 速度比較
print(timeit.timeit(using_for, number=10000)) # 約0.8秒
print(timeit.timeit(using_comprehension, number=10000)) # 約0.5秒
# 内包表記の方が速い
walrus演算子との組み合わせ(Python 3.8+)
# 計算結果を再利用
# 従来
results = []
for x in range(10):
y = expensive_calculation(x)
if y > 5:
results.append(y)
# walrus演算子を使用
results = [y for x in range(10) if (y := expensive_calculation(x)) > 5]
# 実例:正規表現マッチング
import re
text = "user: alice, user: bob, admin: charlie"
pattern = r"user: (\w+)"
users = [m.group(1) for s in text.split(", ") if (m := re.match(pattern, s))]
print(users) # ['alice', 'bob']
まとめ
| 内包表記 | 構文 | 結果 |
|---|---|---|
| リスト | [x for x in ...] | list |
| 辞書 | {k: v for k, v in ...} | dict |
| 集合 | {x for x in ...} | set |
| ジェネレータ | (x for x in ...) | generator |
- 内包表記はPythonらしい簡潔なコード
- 複雑になりすぎたら通常のfor文を使う
- 大量データにはジェネレータ式を使う
ifで条件フィルタ、if-elseで値の変換
次回はジェネレータとイテレータについて学びます。