チュートリアル

Python応用構文:内包表記

Python内包表記応用
広告エリア

はじめに

内包表記(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で値の変換

次回はジェネレータとイテレータについて学びます。

広告エリア