タイトル: 辞書の作成
SEOタイトル: Python 辞書 (dict) 作成方法完全ガイド
| この記事の要点 |
|
Python 辞書 (dict) とは
辞書 (dictionary, dict) は Python の組み込みコレクションの 1 つで、キー (key) と値 (value) のペアを格納するデータ構造です。ハッシュテーブルで実装されており、検索・挿入・削除が平均 O(1) です。
Python 3.7 以降は挿入順序が保持されることが公式に保証されています(CPython 3.6 から実装上は保持)。
1. 辞書リテラル {} で作る
# 基本
user = {"name": "Alice", "age": 30, "email": "alice@example.com"}
# 空の辞書
empty = {}
# ネスト
config = {
"database": {
"host": "localhost",
"port": 5432,
},
"cache": {
"ttl": 300,
},
}
# キーは文字列以外も OK (ハッシュ可能なら何でも)
mixed = {
1: "one",
(1, 2): "tuple-key",
"name": "Alice",
True: "bool-key", # 注: True == 1 なので 1: と衝突
}
2. dict() コンストラクタで作る
# キーワード引数 (キーは文字列のみ)
user = dict(name="Alice", age=30, email="alice@example.com")
# ペアのリスト / イテラブル
user = dict([("name", "Alice"), ("age", 30)])
# 既存辞書をコピー
copy = dict(original)
# 別の辞書 + 追加
extended = dict(original, extra=42)
# zip と組合せ
keys = ["name", "age", "email"]
values = ["Alice", 30, "alice@example.com"]
user = dict(zip(keys, values))
3. dict.fromkeys() で同じ値を持つ辞書
# 全部 0 で初期化
counts = dict.fromkeys(["a", "b", "c"], 0)
# {"a": 0, "b": 0, "c": 0}
# デフォルト値省略 → None
flags = dict.fromkeys(["x", "y", "z"])
# {"x": None, "y": None, "z": None}
# ⚠️ 落とし穴: 可変オブジェクトを共有してしまう
bad = dict.fromkeys(["a", "b"], [])
bad["a"].append(1)
print(bad)
# {"a": [1], "b": [1]} ← 同じリスト!
# ✅ 修正: 内包表記で個別オブジェクト
good = {k: [] for k in ["a", "b"]}
4. 辞書内包表記
# 基本形
squares = {n: n * n for n in range(5)}
# {0: 0, 1: 1, 2: 4, 3: 9, 4: 16}
# 既存リストから生成
words = ["apple", "banana", "cherry"]
length_map = {w: len(w) for w in words}
# {"apple": 5, "banana": 6, "cherry": 6}
# 条件付き
even_squares = {n: n * n for n in range(10) if n % 2 == 0}
# {0: 0, 2: 4, 4: 16, 6: 36, 8: 64}
# キーと値を入れ替え
original = {"a": 1, "b": 2, "c": 3}
swapped = {v: k for k, v in original.items()}
# {1: "a", 2: "b", 3: "c"}
# ネスト内包
matrix = {(i, j): i * 10 + j for i in range(3) for j in range(3)}
5. 辞書のマージ
a = {"x": 1, "y": 2}
b = {"y": 20, "z": 30}
# Python 3.5+ : アンパック展開
merged = {**a, **b}
# {"x": 1, "y": 20, "z": 30} ← 後勝ち
# Python 3.9+ : | 演算子
merged = a | b
# {"x": 1, "y": 20, "z": 30}
# in-place マージ
a |= b
# a == {"x": 1, "y": 20, "z": 30}
# 旧来の方法
merged = a.copy()
merged.update(b)
# 衝突時に値をマージしたい場合
def deep_merge(a, b):
result = a.copy()
for k, v in b.items():
if k in result and isinstance(result[k], dict) and isinstance(v, dict):
result[k] = deep_merge(result[k], v)
else:
result[k] = v
return result
6. collections の派生型
defaultdict: 自動初期化
from collections import defaultdict
# 値が無いキーアクセスで自動初期化
counts = defaultdict(int)
for word in ["apple", "banana", "apple", "cherry"]:
counts[word] += 1
# defaultdict(int, {"apple": 2, "banana": 1, "cherry": 1})
groups = defaultdict(list)
for name, dept in [("Alice", "Eng"), ("Bob", "Sales"), ("Carol", "Eng")]:
groups[dept].append(name)
# {"Eng": ["Alice", "Carol"], "Sales": ["Bob"]}
# defaultdict(set), defaultdict(dict) も同様
Counter: 要素数カウント
from collections import Counter
c = Counter("mississippi")
# Counter({"i": 4, "s": 4, "p": 2, "m": 1})
c.most_common(2)
# [("i", 4), ("s", 4)]
# 加算・減算もできる
c1 = Counter(a=3, b=1)
c2 = Counter(a=1, b=2)
c1 + c2 # Counter({"a": 4, "b": 3})
c1 - c2 # Counter({"a": 2})
OrderedDict: 明示的な順序保証
from collections import OrderedDict
# Python 3.7+ では通常の dict も順序保持
# OrderedDict 固有機能: move_to_end, popitem(last=False)
od = OrderedDict([("a", 1), ("b", 2), ("c", 3)])
od.move_to_end("a")
# OrderedDict([("b", 2), ("c", 3), ("a", 1)])
od.popitem(last=False) # 先頭から取り出し
# ("b", 2)
ChainMap: 複数辞書を 1 つに見せる
from collections import ChainMap
defaults = {"theme": "light", "lang": "en"}
user_pref = {"theme": "dark"}
config = ChainMap(user_pref, defaults)
config["theme"] # "dark" (user_pref を先に見る)
config["lang"] # "en" (defaults から取得)
7. TypedDict (型ヒント付き辞書)
from typing import TypedDict, NotRequired
class User(TypedDict):
name: str
age: int
email: NotRequired[str] # Python 3.11+
def create_user(data: User) -> None:
print(data["name"])
# 使用
create_user({"name": "Alice", "age": 30})
create_user({"name": "Bob", "age": 25, "email": "bob@example.com"})
# 静的型チェッカー (mypy / pyright) が型ミスを検出
# create_user({"name": "Charlie"}) # ← age 必須エラー
8. JSON との相互変換
import json
# dict → JSON 文字列
user = {"name": "Alice", "age": 30}
json_str = json.dumps(user, indent=2, ensure_ascii=False)
# JSON 文字列 → dict
data = json.loads(json_str)
# ファイル
with open("config.json") as f:
config = json.load(f)
with open("output.json", "w", encoding="utf-8") as f:
json.dump(config, f, indent=2, ensure_ascii=False)
# 日本語を含む場合は ensure_ascii=False
9. キーアクセスのパターン
user = {"name": "Alice", "age": 30}
# 基本アクセス(キーが無いと KeyError)
name = user["name"]
# get(): キーが無ければ None または指定値
age = user.get("age") # 30
email = user.get("email") # None
email = user.get("email", "") # "" (デフォルト)
# setdefault(): キーが無ければ初期化して値返す
groups = {}
groups.setdefault("eng", []).append("Alice")
# {"eng": ["Alice"]}
# pop(): キー削除して値取得
age = user.pop("age") # 30, user から削除
age = user.pop("age", None) # None (キー無くてもエラーなし)
# in 演算子
if "name" in user:
print(user["name"])
# items / keys / values
for k, v in user.items():
print(f"{k} = {v}")
各作成方法の使い分け
| 場面 | 推奨方法 |
|---|---|
| 固定キー・固定値 | リテラル {} |
| キーが Python 識別子のみ | dict(key=value) |
| キーと値のリストから生成 | dict(zip(keys, values)) |
| すべて同じ初期値 | dict.fromkeys(keys, init) (immutable のみ) |
| 動的生成 | 辞書内包表記 |
| 自動初期化が欲しい | defaultdict |
| カウント用途 | Counter |
| API レスポンスの型保証 | TypedDict / dataclass |
FAQ
Q: dict と OrderedDict の違いは?
A: Python 3.7+ では順序保持は両方同じ。OrderedDict は move_to_end, popitem(last=False), == が順序を考慮するなど追加機能あり。
Q: 辞書のキーに list は使えない?
A: 使えません。キーはハッシュ可能 (hashable)である必要があり、list / set / dict は不可。tuple や frozenset は OK。
Q: dict と dataclass、どちらを使うべき?
A: 構造が決まっているなら dataclass / TypedDict が型安全で IDE 補完も効く。動的なキーを扱うなら dict。
Q: 大量データの dict は効率的?
A: 平均 O(1) アクセスで高速ですが、メモリ消費は list より大きい。10 万件超なら専用 KVS(Redis 等)も検討。