11.

Python dict 完全ガイド(生成・get・内包表記・マージ・defaultdict/Counter/TypedDict)

編集
この記事の要点
  • dict は Python のキー/値ペアコレクション。{"name": "太郎", "age": 30} のように作成
  • 安全アクセスは d.get(key, default)d[key] は未存在キーで KeyError
  • items() / keys() / values() でイテレーション。3.7+ は挿入順保証
  • 辞書内包表記 {k: v for k, v in pairs}、マージは {**a, **b} (3.5+) / a | b (3.9+)
  • 便利な派生型: defaultdict(未存在キー自動生成)/ OrderedDict(順序操作 API)/ Counter(カウント特化)/ TypedDict(型ヒント付き dict)

dict とは

dict は Python 標準のキー/値ペアを格納するハッシュマップ型です。リスト(list)が「順序付きの値の並び」なのに対し、dict はキーで値を取り出す連想配列。検索・挿入が平均 O(1) で、用途は設定値・JSON 解析結果・カウンタ・キャッシュなど極めて広範です。

Python 3.7 以降は挿入順が保証される仕様になっており、それ以前の「順序不定」は実質過去の話です(CPython 3.6 で実装、3.7 で言語仕様化)。

基本: 作成・参照・追加・削除

# 作成
user = {"name": "太郎", "age": 30, "email": "taro@example.com"}

# dict() 関数でも作成可能
user2 = dict(name="花子", age=25)

# 空 dict
empty = {}        # こちらが推奨
empty2 = dict()

# 参照
print(user["name"])         # 太郎
# print(user["unknown"])    # KeyError 例外

# 安全参照(推奨)
print(user.get("unknown"))           # None
print(user.get("unknown", "default")) # default

# 追加・更新
user["age"] = 31              # 更新
user["address"] = "東京"       # 追加

# 削除
del user["email"]
user.pop("address")           # 値を返す
user.pop("notfound", None)    # 未存在でも None で安全

# 存在判定
if "name" in user:
    print("name キーあり")

イテレーション

d = {"a": 1, "b": 2, "c": 3}

# キーで回す(デフォルト)
for k in d:
    print(k, d[k])

# キー
for k in d.keys():
    print(k)

# 値
for v in d.values():
    print(v)

# キー + 値(最もよく使う)
for k, v in d.items():
    print(f"{k}={v}")

# ソートしてイテレート
for k in sorted(d):
    print(k, d[k])

# 値でソート
for k, v in sorted(d.items(), key=lambda x: x[1], reverse=True):
    print(k, v)

辞書内包表記

# リストから dict 生成
nums = [1, 2, 3, 4, 5]
squares = {n: n*n for n in nums}
# {1: 1, 2: 4, 3: 9, 4: 16, 5: 25}

# 2 つのリストから
keys = ["a", "b", "c"]
vals = [10, 20, 30]
d = {k: v for k, v in zip(keys, vals)}
# zip だけなら dict(zip(keys, vals)) でも同じ

# 条件付き
even_squares = {n: n*n for n in nums if n % 2 == 0}
# {2: 4, 4: 16}

# キーと値の入れ替え
inverted = {v: k for k, v in d.items()}

# JSON / 環境変数のフィルタリング
import os
debug_envs = {k: v for k, v in os.environ.items() if k.startswith("DEBUG_")}

マージ(複数 dict の結合)

記法バージョン説明
{**a, **b}3.5+アンパックでマージ。b の値が a を上書き
a | b3.9+パイプ演算子。最も簡潔
a |= b3.9+a を破壊的に更新
a.update(b)全バージョンa を破壊的に更新
dict(a, **b)全バージョン古い書き方
a = {"x": 1, "y": 2}
b = {"y": 99, "z": 3}

# 1. アンパック (3.5+)
merged = {**a, **b}
# {"x": 1, "y": 99, "z": 3}

# 2. パイプ演算子 (3.9+)
merged2 = a | b

# 3. update(a が変わる)
a.update(b)
# a == {"x": 1, "y": 99, "z": 3}

collections モジュールの便利な派生型

from collections import defaultdict, OrderedDict, Counter

# defaultdict: 未存在キーで KeyError にならず、自動でデフォルト値生成
counts = defaultdict(int)
for word in ["apple", "banana", "apple"]:
    counts[word] += 1
# defaultdict(int, {"apple": 2, "banana": 1})

groups = defaultdict(list)
for student in [("A組", "太郎"), ("B組", "花子"), ("A組", "次郎")]:
    groups[student[0]].append(student[1])
# {"A組": ["太郎", "次郎"], "B組": ["花子"]}

# OrderedDict: 順序操作 API が追加されている
od = OrderedDict([("a", 1), ("b", 2), ("c", 3)])
od.move_to_end("a")           # a を末尾へ
od.popitem(last=False)        # 先頭を pop

# Counter: 出現回数カウント特化
c = Counter("mississippi")
# Counter({"i": 4, "s": 4, "p": 2, "m": 1})
c.most_common(2)
# [("i", 4), ("s", 4)]

TypedDict(型ヒント付き dict)

Python 3.8+ では TypedDictdict のキーと値の型を静的に表現できます。mypy 等の型チェッカーで補完・検証が効きます:

from typing import TypedDict

class User(TypedDict):
    name: str
    age: int
    email: str

def greet(u: User) -> str:
    return f"Hello {u['name']} ({u['age']})"

# OK
greet({"name": "太郎", "age": 30, "email": "taro@example.com"})

# NG(mypy がエラー検出)
greet({"name": "太郎", "age": "thirty", "email": "x"})  # age が str

# 任意キーは total=False
class PartialUser(TypedDict, total=False):
    name: str
    age: int

よく使うイディオム

# 1. 2 つのリストを dict 化
keys, vals = ["a", "b", "c"], [1, 2, 3]
d = dict(zip(keys, vals))

# 2. dict から最大値のキー
prices = {"apple": 100, "banana": 80, "melon": 500}
max_key = max(prices, key=prices.get)   # "melon"

# 3. 辞書をフィルタ
positive = {k: v for k, v in d.items() if v > 0}

# 4. 値を加工
doubled = {k: v*2 for k, v in d.items()}

# 5. JSON 化
import json
s = json.dumps(d, ensure_ascii=False, indent=2)
d2 = json.loads(s)

# 6. 1 行で頻度カウント
from collections import Counter
freq = Counter("abracadabra")

パフォーマンスのコツ

  • キーは hashable な不変オブジェクト(str / int / tuple)が必須。list / dict はキー不可
  • 大量の d[k] = d.get(k, 0) + 1defaultdict(int) または Counter の方が速い
  • キー存在判定は k in d(O(1))。k in d.keys() も同じだが冗長
  • 順序を維持したまま「最近使ったキーを末尾」にしたい → OrderedDict.move_to_end()
  • 1000 万件超の巨大 dict はメモリ消費が大きい → sqlite3 / shelve を検討

FAQ

Q: dict と list、どちらを使う?
A: キーで引きたいなら dict、順番で参照するなら list。dict も 3.7+ で順序保証だが「順序を活用する API」は list の方が豊富。

Q: dict のソート結果も dict ?
A: sorted(d.items())リストを返します。dict のまま欲しければ dict(sorted(d.items()))

Q: ネストした dict のアクセスを安全に
A: d.get("a", {}).get("b", "default")、または pydantic / dataclass + 専用クラス化を検討。

編集
Post Share
子ページ
  1. 辞書の作成
  2. 値の参照
  3. 要素の追加と更新
  4. 要素の削除
  5. キーの存在チェック
  6. dictの中身を確認する方法
同階層のページ
  1. 基本的なルール
  2. 変数
  3. 演算子
  4. 標準ライブラリ
  5. 外部ライブラリ
  6. 制御構文
  7. リスト(配列)
  8. タプル
  9. セット
  10. 辞書(dict)
  11. クラスとメソッド
  12. 継承の概念と必要性
  13. 継承の構文
  14. コンストラクタ
  15. cookieの値の設定と取得
  16. 例外処理
  17. 例外を文字列で出力する方法
  18. httpリクエスト(curl)をする方法
  19. Responseオブジェクトの中身の確認
  20. 変数が空かどうか判定する方法
  21. タイムゾーンの設定と現在日時の取得と文字列化
  22. シングルクォーテーションとダブルクォーテーションの違い