こんにちは、かつコーチです。
前回はリスト内包表記を解説しましたが、今回はその応用編として辞書内包表記とセット内包表記を取り上げます。
Pythonでは、リストだけでなく辞書(dict)やセット(set)についても、内包表記を使って1行で作成できます。
私も最初にリスト内包表記に慣れたあと、「辞書やセットも同じ感覚で書けるだろう」と思って書いたところ、記号の違いでエラーになった経験があります。
この記事では、辞書内包表記・セット内包表記の基本構文と、リスト内包表記との違いを、実際のコード例とともに解説します。
辞書内包表記の基本
辞書内包表記とは
辞書内包表記とは、for文を使った辞書の生成処理を、1行で簡潔に書ける構文です。
まず、通常のfor文で辞書を作る場合を見てみます。
fruits = ["りんご", "みかん", "ぶどう"]
fruit_lengths = {}
for fruit in fruits:
fruit_lengths[fruit] = len(fruit)
print(fruit_lengths) # {'りんご': 3, 'みかん': 3, 'ぶどう': 3}
これを辞書内包表記で書き直すと、次のようになります。
fruits = ["りんご", "みかん", "ぶどう"]
fruit_lengths = {fruit: len(fruit) for fruit in fruits}
print(fruit_lengths) # {'りんご': 3, 'みかん': 3, 'ぶどう': 3}
辞書内包表記の基本構文は、次の形です。
{キー: 値 for 変数 in イテラブル}
リスト内包表記との一番の違いは、角括弧[]ではなく波括弧{}を使う点と、式の部分がキー: 値という2つの要素になる点です。
既存の2つのリストから辞書を作る
辞書内包表記は、zip()関数と組み合わせて使われることも多くあります。
zip()とは、複数のリストの同じ順番の要素同士をペアにする関数です。
names = ["田中", "佐藤", "鈴木"]
scores = [80, 92, 75]
score_dict = {name: score for name, score in zip(names, scores)}
print(score_dict) # {'田中': 80, '佐藤': 92, '鈴木': 75}
zip(names, scores)によって、("田中", 80)のようなペアが順番に作られ、それをname: scoreという形で辞書のキーと値に振り分けています。
CSVファイルやAPIから受け取った2つのリストを辞書にまとめたいときなど、実務でもよく使われるパターンです。
セット内包表記の基本
セット内包表記とは
セット内包表記とは、for文を使ったセット(重複を許さないデータの集合)の生成処理を、1行で書ける構文です。
numbers = [1, 2, 2, 3, 4, 4, 4, 5]
unique_squares = {n ** 2 for n in numbers}
print(unique_squares) # {1, 4, 9, 16, 25}
構文は次の形です。
{式 for 変数 in イテラブル}
見た目は辞書内包表記と同じ波括弧を使いますが、キー: 値のペアではなく、リスト内包表記と同じ単一の式を書く点が特徴です。
このコードでは、numbersの中に重複した2や4が含まれていますが、セットの性質上、結果のunique_squaresには重複した値が自動的に1つにまとめられています。
「重複を除きながら加工したい」というときに、セット内包表記が便利です。
リスト・辞書・セット内包表記の違い
構文の見分け方
3つの内包表記は見た目がよく似ているため、違いを表にまとめておきます。
| 種類 | 括弧 | 式の形 | 結果 |
|---|---|---|---|
| リスト内包表記 | [] | 式 | [1, 2, 3]のようなリスト |
| 辞書内包表記 | {} | キー: 値 | {"a": 1}のような辞書 |
| セット内包表記 | {} | 式 | {1, 2, 3}のようなセット |
辞書とセットはどちらも波括弧{}を使うため、「コロン(:)があるかどうか」が両者を見分ける一番のポイントになります。
コロンがあれば辞書内包表記、なければセット内包表記だと覚えておきましょう。
よくあるつまずきポイント・エラー対処
つまずき1:セットのつもりが辞書になっていた(空の場合)
セットと辞書の見分けについて、特に注意が必要なのが空のコレクションを作るときです。
❌ Before:空のセットを作ったつもりが辞書になっていた
empty = {}
print(type(empty)) # <class 'dict'>
私は初めてこの挙動を知ったとき、「波括弧なのだから当然セットになるはず」と思い込んでいて、意図せず辞書型の変数を作ってしまったことがあります。
その状態でempty.add("apple")のようにセット用のメソッドを呼び出そうとして、エラーに気づきました。
AttributeError: 'dict' object has no attribute 'add'
Pythonでは、波括弧{}単体は辞書を作る記法として予約されているため、空のセットをこの書き方で作ることはできません。
✅ After:空のセットはset()で作る
empty_set = set()
print(type(empty_set)) # <class 'set'>
empty_set.add("apple")
print(empty_set) # {'apple'}
空のセットを作りたいときは、{}ではなくset()という組み込み関数を使う必要があります。
内包表記自体は{式 for ... }のようにセットとして正しく解釈されますが、要素が1つもない{}だけは辞書扱いになるという例外ルールを覚えておきましょう。
つまずき2:辞書内包表記でキーが重複して値が消える
辞書内包表記特有のつまずきとして、キーの重複による値の上書きがあります。
❌ Before:キーが重複して意図した件数より少なくなる
orders = [
{"customer": "田中", "amount": 1000},
{"customer": "佐藤", "amount": 2000},
{"customer": "田中", "amount": 1500}, # 田中さんが2件目の注文
]
order_dict = {order["customer"]: order["amount"] for order in orders}
print(order_dict) # {'田中': 1500, '佐藤': 2000}
このコードを実行すると、田中さんの最初の注文(1000円)が、2件目の注文(1500円)で上書きされて消えてしまいます。
実際に私も、顧客名をキーにした注文データの集計処理でこのパターンにはまり、「合計金額が明らかに少ない」と気づいて原因を調べたところ、辞書のキー重複が原因でした。
辞書のキーは重複を許さないため、同じキーが複数回登場すると、後から処理された値で上書きされてしまいます。
✅ After:キーが重複する可能性がある場合はリストでまとめる
from collections import defaultdict
orders = [
{"customer": "田中", "amount": 1000},
{"customer": "佐藤", "amount": 2000},
{"customer": "田中", "amount": 1500},
]
order_dict = defaultdict(list)
for order in orders:
order_dict[order["customer"]].append(order["amount"])
print(dict(order_dict)) # {'田中': [1000, 1500], '佐藤': [2000]}
defaultdictとは、存在しないキーにアクセスしたときに自動で初期値(この場合は空リスト)を用意してくれる、辞書の拡張版です。
キーが重複する可能性があるデータを扱うときは、辞書内包表記をそのまま使うのではなく、値をリストでまとめる設計に切り替えることで、データの欠落を防げます。
応用・一歩先の使い方
辞書内包表記で既存の辞書を加工する
辞書内包表記は、既存の辞書を変換する場面でもよく使われます。
prices = {"りんご": 100, "みかん": 80, "ぶどう": 300}
# 全商品を10%値上げした新しい辞書を作る
discounted_prices = {name: int(price * 1.1) for name, price in prices.items()}
print(discounted_prices) # {'りんご': 110, 'みかん': 88, 'ぶどう': 330}
prices.items()によって、辞書のキーと値のペアを同時に取り出せます。
元の辞書を直接書き換えるのではなく、新しい辞書を作るという発想がポイントです。
条件を組み合わせれば、特定の商品だけを抽出することもできます。
prices = {"りんご": 100, "みかん": 80, "ぶどう": 300}
# 200円以上の商品だけを抽出
expensive_items = {name: price for name, price in prices.items() if price >= 200}
print(expensive_items) # {'ぶどう': 300}
3つの内包表記を使い分ける判断軸
最後に、リスト・辞書・セットの内包表記をどう使い分けるかを整理しておきます。
- 順序が意味を持つデータを作りたい → リスト内包表記
- キーと値のペアでデータを管理したい → 辞書内包表記
- 重複を除いた集合を作りたい・存在確認を高速に行いたい → セット内包表記
「作りたいデータの形」から逆算して、どの内包表記を使うべきかを判断する習慣をつけると、コードの意図がより明確になります。
まとめ
この記事のポイント
- 辞書内包表記は
{キー: 値 for 変数 in イテラブル}という構文で書く - セット内包表記は
{式 for 変数 in イテラブル}という構文で、辞書内包表記との違いは「コロンの有無」 - 空のコレクションを作るとき、
{}は辞書になるため、空のセットはset()で作る - 辞書内包表記はキーが重複すると値が上書きされるため、重複の可能性がある場合は
defaultdictなどで対応する - リスト・辞書・セットは、作りたいデータの形(順序・ペア・重複なし集合)から逆算して使い分ける
次に読むべき記事
内包表記の基本が身についたら、次はスコープや変数の扱い方も復習しておきましょう。
→ 次の記事:Pythonのスコープとグローバル変数・ローカル変数の違い
タグ: Python, 中級者向け, 基本文法