C
Python/中級/Lesson 09

リスト内包表記

30分·theory
このチャプター
1/8
Python

リスト内包表記

🎯 このレッスンを読み終えたら

このレッスンを読み終えると、以下の3つを自信を持ってできるようになります。

  • ✅ リストのスライシング + 内包表記
  • ✅ list と tuple の使い分け + 深いコピー (copy.deepcopy)
  • sortsorted の違い(元のリストが変更されるかどうか)

学習目標をチェックリストとして手元に置き、すべてに答えられるようになったらレッスンを閉じてください。

リスト内包表記6パターン — コード + 実行結果

[式 for 変数 in イテラブル if 条件] — リストを1行で作る構文。Pythonのアイデンティティ。


1. 最もシンプルな形

python
# 通常のfor文
二乗 = []
for x in range(1, 6):
    二乗.append(x * x)
print(二乗)              # [1, 4, 9, 16, 25]

# list comprehension — 一行
二乗 = [x * x for x in range(1, 6)]
print(二乗)              # [1, 4, 9, 16, 25]

同じ結果を1行で。可読性が高い(変数 → 変換 → 出処の順)。


2. 条件を追加 — if

python
数字たち = [1, 2, 3, 4, 5, 6, 7, 8]

偶数 = [x for x in 数字たち if x % 2 == 0]
print(偶数)              # [2, 4, 6, 8]

大きい_偶数の_二乗 = [x * x for x in 数字たち if x % 2 == 0 and x > 4]
print(大きい_偶数の_二乗)    # [36, 64]

ifフィルター。式を適用する前に通過するかどうかを決定します。


3. 変換 — 文字列・dict への応用

python
名前たち = ["山田太郎", "鈴木一郎", "田中花子"]

大文字 = [n.upper() for n in 名前たち]      # 日本語は影響なし
長さ = [len(n) for n in 名前たち]
挨拶 = [f"こんにちは {n}" for n in 名前たち]

print(挨拶)              # ['こんにちは 山田太郎', 'こんにちは 鈴木一郎', 'こんにちは 田中花子']
print(長さ)              # [4, 4, 4]

4. dict / set 内包表記

python
名前たち = ["山田太郎", "鈴木一郎", "田中花子"]

# dict comprehension — {key: value for ...}
長さマップ = {n: len(n) for n in 名前たち}
print(長さマップ)            # {'山田太郎': 4, '鈴木一郎': 4, '田中花子': 4}

# set comprehension — {value for ...}
文字_set = {文字 for 名前 in 名前たち for 文字 in 名前}
print(文字_set)          # {'山', '田', '太', '郎', '鈴', '木', '一', '郎', '田', '中', '花', '子'}

5. ネスト(二重for) — 慎重に

python
# 九九 — 2の段・3の段のすべての組み合わせ
九九 = [(i, j, i*j) for i in range(2, 4) for j in range(1, 5)]
print(九九[:4])
# [(2, 1, 2), (2, 2, 4), (2, 3, 6), (2, 4, 8)]

⚠️ 3重以上のネストは読みにくい — 通常のforループの方が明確です。


6. 悪い例 — 内包表記の乱用

python
# ❌ 複雑すぎる — 6行のものを1行に
結果 = [(i, j) for i in range(10) for j in range(10) if i + j > 10 and i != j]

# ✅ 通常のfor文がより明確
結果 = []
for i in range(10):
    for j in range(10):
        if i + j > 10 and i != j:
            結果.append((i, j))

ルール: 二重forに条件が2つ以上あれば通常のforループを使う。


一行まとめ

パターン構文
変換のみ[f(x) for x in xs]
変換 + フィルター[f(x) for x in xs if cond(x)]
dict{k: v for x in xs}
set{f(x) for x in xs}

ポイント: シンプルな場合にのみ使用する。3行を超えるなら通常のforループへ。

💻 悪い例 — 複雑なネスト内包表記
# 読みにくいネストした内包表記
result = [x*y for sublist in [[1,2],[3,4],[5,6]] for x in sublist for y in range(x) if y % 2 == 0]
# 何を意味するのか即座に把握できない

# 不要な内包表記 — 単純な合計には sum() を使用
total = sum([x**2 for x in range(100)])  # リスト生成は不要
# 代わりに: sum(x**2 for x in range(100)) — ジェネレータを直接
💻 良い例 — 内包表記4種の活用
# リスト内包表記
squares = [x**2 for x in range(10)]
even_squares = [x**2 for x in range(10) if x % 2 == 0]
print(even_squares)  # [0, 4, 16, 36, 64]

# 条件式 (三項)
labels = ['偶数' if x % 2 == 0 else '奇数' for x in range(5)]
print(labels)  # ['偶数', '奇数', '偶数', '奇数', '偶数']

# 辞書内包表記
users = [{'id': 1, 'name': '山田太郎'}, {'id': 2, 'name': '鈴木一郎'}]
user_map = {u['id']: u['name'] for u in users}  # {1: '山田太郎', 2: '鈴木一郎'}

# 辞書のキー/値の入れ替え
original = {'a': 1, 'b': 2, 'c': 3}
swapped = {v: k for k, v in original.items()}  # {1: 'a', 2: 'b', 3: 'c'}

# セット内包表記 — 重複自動除去
words = ['apple', 'banana', 'apple', 'cherry']
first_letters = {w[0] for w in words}  # {'a', 'b', 'c'}

# ジェネレータ式 — メモリ効率
total = sum(x**2 for x in range(1_000_000))  # リストは生成しない

# ネストした内包表記 — 2段までは許容範囲
matrix = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
flat = [x for row in matrix for x in row]  # [1, 2, 3, 4, 5, 6, 7, 8, 9]

# ネストが3段以上の場合はfor文で
cube_coords = []
for x in range(3):
    for y in range(3):
        for z in range(3):
            cube_coords.append((x, y, z))  # 可読性優先

🐍 実際に試してみよう — リスト内包表記

上記の概念を実際にコードとして実行してみてください。値を変えながら動作を自分で確認するのが最も効果的な学習法です。
✏️ Python 코드
📟 コンソール出力
▶ 実行ボタンを押してください
🐍 Pyodideで実際のPythonを実行 — 初回は読み込みに3〜5秒

🤖 AIにこう依頼してみよう

このレッスンの概念を知っていれば、AIに具体的な指示が出せます。漠然とした「直して」ではなく、用語を使ったリクエスト — それがトークン節約の出発点です。

  • 「このfor + appendをリスト内包表記に書き換えて」
  • 「このコードで深いコピー(copy.deepcopy)が必要か確認して」

なぜトークンが減るのか

概念を知らないと、AIの回答を受け取っても「それって何ですか?」と聞き直す必要があります。その「聞き直し」がトークンを消費します。概念を一度覚えれば、会話が一度で完結します。

先に読むとよい概念: set — 重複のない集合
次のおすすめ: ラムダ関数
リスト内包表記 - Python