# Pythonの辞書（dict）：作成、メソッド、ループ、ソート、frozendict

URL: https://skillaria.top/ja/guides/python/slovari-python

Pythonの辞書（dict）は、「キーと値」のペアを保持する変更可能なコレクションです。キーを使うと値をほぼ瞬時に取り出せます。波かっこ {"コーヒー": 250} や dict() 関数で作成します。キーは一意で、ハッシュ可能でなければなりません。Python 3.7からは挿入順が保たれ、Python 3.15には変更できない組み込みの frozendict が加わりました。

Pythonの辞書（`dict`）は、「キーと値」のペアを保持するコレクションです。登録されている件数がどれだけ多くても、キーを使えば値をほぼ瞬時に取り出せます。カテゴリごとの支出を集計する、設定を保存する、ユーザーIDからユーザーを検索する、といった場面で辞書を使います。ここでは、辞書の作成、メソッド、ループ、ソート、結合、そしてPython 3.15の新機能 `frozendict` まで、辞書を扱うのに必要なことを一通り紹介します。

## Pythonの辞書とは

辞書は、キーが一意になるように値を保持します。既存のキーに値を代入すると、古い値が置き換えられます。内部ではハッシュテーブルが使われているため、キーでの検索・追加・削除は、全体を順に調べるのではなく、平均して一定の時間で完了します。

キーにできるのは、ハッシュ可能なオブジェクトだけです。文字列、数値、変更できない値だけからなるタプルなどです。リストはキーにできません。その場合は `TypeError: unhashable type: 'list'` が出ます。一方、値には何でも使え、別の辞書やリストも入れられます。

Python 3.7以降では、辞書が挿入順を保証します。ループで回すと、キーは追加された順に現れます。

## 辞書の作成方法

辞書を作る方法はいくつかあり、どれも実際のコードで使われています。

```
spent = {"食費": 1200, "タクシー": 450}   # リテラル
empty = {}                               # 空の辞書
also_empty = dict()                      # dict 関数でも同じ
user = dict(name="花子", age=30)         # キーワード引数
pairs = dict([("a", 1), ("b", 2)])       # ペアのリストから
keys = dict.fromkeys(["x", "y"], 0)      # {'x': 0, 'y': 0}
```

注意：空の波かっこ `{}` は空の辞書を作ります。空の集合ではありません。空の集合は `set()` でのみ作れます。

2つのリストから辞書を作るには、`zip` が便利です。

```
names = ["食費", "タクシー", "映画"]
sums = [1200, 450, 600]
spent = dict(zip(names, sums))
print(spent)   # {'食費': 1200, 'タクシー': 450, '映画': 600}
```

## キーで値を取り出す：d[k] と get

値は角かっこで読み出します。キーが存在しない場合、Pythonは `KeyError` を発生させます。エラーを避けたいときは `get` メソッドを使います。キーがなければ `None` か、指定したデフォルト値を返します。

```
spent = {"食費": 1200}
print(spent["食費"])            # 1200
print(spent.get("映画"))        # None
print(spent.get("映画", 0))     # 0
spent["映画"]                   # KeyError: '映画'
```

キーが存在するかどうかは、`in` 演算子で確認できます。例：`"食費" in spent` は `True` を返します。`in` が確認するのはキーであり、値ではありません。値を確認するには `1200 in spent.values()` と書きます。

## 追加、変更、削除

項目の追加や更新は、代入1行で済みます。件数を数えるときは、`get` と組み合わせると便利です。

```
spent = {}
for category, amount in [("食費", 250), ("タクシー", 300), ("食費", 400)]:
    spent[category] = spent.get(category, 0) + amount
print(spent)   # {'食費': 650, 'タクシー': 300}
```

キーを削除するには、`del d[k]` を使います。キーがないと `KeyError` になります。または、`d.pop(k, default)` を使うと、削除と同時に値を取得できます。辞書を空にするには `d.clear()`、要素数（ペアの数）を調べるには `len(d)` を使います。

## 辞書のメソッド一覧

| メソッド | 役割 |
| --- | --- |

| `get(k, default)` | キーの値を返す。なければ default を返し、エラーにはならない |

| `setdefault(k, default)` | 値を返す。キーがなければ、先に default を書き込んでから返す |

| `update(other)` | 別の辞書のペアを追加し、既存のキーは上書きする |

| `pop(k, default)` | キーを削除して値を返す |

| `popitem()` | 最後に追加されたペアを削除して返す |

| `keys()`、`values()`、`items()` | キー、値、ペアのビュー |

| `copy()` | 浅いコピーを作る |

| `clear()` | 辞書を空にする |

| `fromkeys(keys, value)` | 同じ値を持つ新しい辞書を作る |

### setdefault：値をリストで埋める

値がリストで、要素をグループ化したいときは、`setdefault` が役立ちます。

```
groups = {}
for item, cat in [("コーヒー", "食費"), ("電車", "交通費"), ("ピザ", "食費")]:
    groups.setdefault(cat, []).append(item)
print(groups)   # {'食費': ['コーヒー', 'ピザ'], '交通費': ['電車']}
```

### update：辞書を更新する

`update` は辞書をその場で変更します。新しいキーは追加され、既存のキーの値は上書きされます。

```
cfg = {"debug": False, "lang": "ja"}
cfg.update({"debug": True, "theme": "dark"})
print(cfg)   # {'debug': True, 'lang': 'ja', 'theme': 'dark'}
```

## defaultdict：初期値を持つ辞書

`collections.defaultdict` は dict のサブクラスで、存在しないキーにアクセスすると、ファクトリ関数を使って値を自動的に作成します。集計やグループ化のコードが短くなります。

```
from collections import defaultdict

spent = defaultdict(int)
for cat, amount in [("食費", 250), ("タクシー", 300), ("食費", 400)]:
    spent[cat] += amount
print(dict(spent))   # {'食費': 650, 'タクシー': 300}

groups = defaultdict(list)
groups["食費"].append("コーヒー")
```

単純に個数を数えるだけなら、`collections.Counter` もあります。例：`Counter(["a", "b", "a"])` は `Counter({'a': 2, 'b': 1})` を返します。

## 辞書のループ：for文

`for` で辞書を回すと、デフォルトではキーが順に取り出されます。キーと値の両方が必要なら、`items()` をループするのが最も一般的です。

```
spent = {"食費": 650, "タクシー": 300}

for cat in spent:                    # キー
    print(cat)

for amount in spent.values():        # 値
    print(amount)

for cat, amount in spent.items():    # ペア
    print(f"{cat}: {amount}円")
```

ループの途中で辞書の要素数を変えることはできません。`RuntimeError: dictionary changed size during iteration` が発生します。一部のキーを削除したい場合は、コピーをループしてください。例：`for k in list(d):`。

## 辞書を画面に表示する

`print(d)` は辞書を1行で表示します。大きなデータや入れ子のデータには、`pprint` モジュールか、`json.dumps(d, ensure_ascii=False, indent=2)` が便利です。インデント付きで、日本語もエスケープされずに表示されます。

## 辞書の並べ替え

辞書そのものはその場で並べ替えられません。`sort` メソッドはないためです。辞書を並べ替えるとは、`sorted` で並べ替えたペアから新しい辞書を作ることです。

```
spent = {"タクシー": 300, "食費": 650, "映画": 120}

by_key = dict(sorted(spent.items()))
print(by_key)    # {'映画': 120, '食費': 650, 'タクシー': 300}

by_value = dict(sorted(spent.items(), key=lambda p: p[1], reverse=True))
print(by_value)  # {'食費': 650, 'タクシー': 300, '映画': 120}
```

上位N件のカテゴリだけが必要なら、結果を辞書に戻す必要はありません。`sorted(spent.items(), key=lambda p: p[1], reverse=True)[:2]` は、ペアのリストを返します。

辞書のリストを、ある項目で並べ替えることもできます。例：`sorted(users, key=lambda u: u["age"])`。

## 辞書の結合

Python 3.9以降では、辞書を `|` 演算子で結合でき、`|=` を使うとその場で更新できます。キーが重なった場合は、右側の値が優先されます。

```
a = {"x": 1, "y": 2}
b = {"y": 20, "z": 3}
print(a | b)          # {'x': 1, 'y': 20, 'z': 3}
print({**a, **b})     # アンパックを使った同じ結果
a |= b                # a.update(b) と同じ
```

注意：辞書同士を `a + b` で足すことはできず、`TypeError` になります。同じキーの値を足し合わせたい場合は、`Counter` を使います。例：`Counter(a) + Counter(b)`。

### 辞書のアンパック

2つのアスタリスク `**` は、辞書を関数の名前付き引数や別の辞書に展開します。

```
def greet(name, city):
    return f"{name}さん（{city}）"

data = {"name": "太郎", "city": "大阪"}
print(greet(**data))   # 太郎さん（大阪）
```

## 辞書内包表記

辞書内包表記は、任意の並びから1行で辞書を作ります。

```
squares = {n: n * n for n in range(5)}
print(squares)   # {0: 0, 1: 1, 2: 4, 3: 9, 4: 16}

big = {k: v for k, v in spent.items() if v > 200}

inverted = {v: k for k, v in {"a": 1, "b": 2}.items()}
print(inverted)  # {1: 'a', 2: 'b'}
```

最後の例は、キーと値を入れ替えて辞書を反転させる方法です。値が一意でハッシュ可能なときにしか使えません。そうでないと、一部のペアが失われます。

## 入れ子の辞書

値として別の辞書を入れることもできます。APIのJSON応答や、ユーザーごとの辞書のような構造を表すときに使います。

```
users = {
    101: {"name": "花子", "skills": ["python", "sql"]},
    102: {"name": "健太", "skills": ["go"]},
}
print(users[101]["skills"][0])            # python
print(users.get(103, {}).get("name"))     # None、KeyError は発生しない
```

## 辞書のコピー：コピーと別名

`b = a` は辞書をコピーせず、同じオブジェクトに2つ目の名前を付けるだけです。`b` を変更すると、`a` にも変更が反映されます。辞書を関数に渡したときも同じことが起きます。関数は共有されたオブジェクトを受け取るため、気づかないうちに中身が書き換えられることがあります。

```
import copy

a = {"limits": [0, 500]}
b = a.copy()              # 浅いコピー
b["limits"].append(1000)  # 入れ子のリストは共有されている！
print(a)                  # {'limits': [0, 500, 1000]}

c = copy.deepcopy(a)      # 完全に独立したコピー
```

## 辞書の比較

`==` は辞書の内容を比較します。すべてのペアが一致すれば等しくなります。キーの順序は考慮されません。例：`{"a": 1, "b": 2} == {"b": 2, "a": 1}` は `True` です。共通のキーを探すには、ビューに対する集合演算を使います。例：`a.keys() & b.keys()`。

## 型付きの辞書：型注釈

辞書の型注釈は `dict[str, int]` のように書きます。フィールドごとに異なる型を持つ固定の構造なら、`typing` モジュールの `TypedDict` が便利です。

```
from typing import TypedDict

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

u: User = {"name": "花子", "age": 30}
```

型注釈は実行時にはチェックされません。IDEやmypyのような型チェッカーが読み取ります。

## 変更できない辞書：Python 3.15のfrozendict

Python 3.15 では、`frozendict` という組み込みの型が追加されました（PEP 814）。インポートは不要で、変更しようとすると即座に `TypeError` になります。以前は、`types` モジュールのラッパーや外部パッケージで変更不可能な辞書を作る必要がありました。誰かが黙って書き換えてはいけない共通設定には、まさにうってつけです。

```
config = frozendict(min_amount=0, max_amount=500)
config["max_amount"] = 1000
# TypeError: 'frozendict' object does not support item assignment

strict = config | {"max_amount": 300}   # 新しい frozendict、元の値は変わらない
cache = {config: "5月のレポート"}        # ハッシュ可能なのでキーに使える
```

講座の資料で押さえておきたい点は次のとおりです。

- `frozendict` は `dict` ではなく `object` を継承しています。そのため `isinstance(cfg, dict)` は `False` を返します。「任意の辞書」かどうかを判定するには、`collections.abc.Mapping`、または `isinstance(x, (dict, frozendict))` を使います。
- ハッシュ可能なのは、キーと値がすべてハッシュ可能な場合だけです。`frozendict(a=[1])` は作成できますが、`hash()` は失敗します。不変性は浅いもので、タプルと同じです。
- 順序は保持されますが、`==` の比較では考慮されません。
- `json.dumps`、`pickle`、`copy` は frozendict に対応しています。

## よくあるミス

- **読み取り時の KeyError**：`get` を使うか、`in` で事前に確認しましょう。
- **キーにリストを使う**：タプルに変換します。例：`d[tuple(lst)]`。
- **ループ中に辞書を変更する**：`list(d)` をループしましょう。
- **代入でコピーができると思い込む**：`copy()` か `deepcopy` を使います。
- **`{}` を空の集合だと思う**：これは空の辞書です。

## まとめ

Pythonの辞書は、言語の中で最も重要なデータ構造の1つです。キーによる高速な検索、挿入順の保持、`get`、`setdefault`、`update` といった便利なメソッド、`items()` によるループ、`sorted` による並べ替え、そして `|` による結合ができます。コピーの扱いとハッシュ可能なキーに注意し、Python 3.15 では設定に `frozendict` を使いましょう。辞書、リスト、タプル、集合は、講座「Python 3.15：初心者からプロへ」の家計簿ツールの例を使った実践的な講義で扱っています。

## FAQ

**辞書の値には異なる型を混在させてもよいですか？**

はい。値は数値、文字列、リスト、別の辞書など、何でも同じ辞書に入れられます。制限はキーだけにあり、キーはハッシュ可能でなければなりません。

**辞書に入っている要素の数はどうやって調べますか？**

len(d) でキーと値のペアの数を返します。空の辞書は 0 になり、if の条件では偽として扱われます。

**dict と defaultdict の違いは何ですか？**

defaultdict は dict のサブクラスで、存在しないキーにアクセスしても KeyError を出さず、int や list などのファクトリで値を作ります。それ以外の動作は通常の辞書と同じです。

**値からキーを探すにはどうしますか？**

直接のメソッドはないので、ループで探します。例：next((k for k, v in d.items() if v == value), None)。頻繁に行うなら、{v: k for k, v in d.items()} で逆向きの辞書を作る方が簡単です。

**辞書をファイルに保存するにはどうしますか？**

多くの場合 json モジュールを使います。書き込み用に開いたファイルに json.dump(d, f, ensure_ascii=False, indent=2) を書き、読み込みには json.load(f) を使います。ただし、キーは文字列として保存されます。


---
Source: Skillaria.Top (https://skillaria.top)
