はじめに
今回は2つの特殊な辞書型を学びます。
OrderedDictは「順序操作」に特化し、ChainMapは「複数の辞書を仮想的に結合」します。
どちらも通常の dict では表現しにくいユースケースを、シンプルに実装できるのが特徴です。
OrderedDict
基本
Python 3.7以降、通常の dict も挿入順を保持します。
それでも OrderedDict が残っている理由は、順序を操作するための明示的なAPIを持っている点にあります。
from collections import OrderedDict
od = OrderedDict()
od['a'] = 1
od['b'] = 2
od['c'] = 3
print(list(od.keys())) # ['a', 'b', 'c']
move_to_end() - 要素を端に移動
from collections import OrderedDict
od = OrderedDict([('a', 1), ('b', 2), ('c', 3)])
# 末尾に移動
od.move_to_end('a')
print(list(od.keys())) # ['b', 'c', 'a']
# 先頭に移動
od.move_to_end('c', last=False)
print(list(od.keys())) # ['c', 'b', 'a']
popitem() - 端から削除
from collections import OrderedDict
od = OrderedDict([('a', 1), ('b', 2), ('c', 3)])
# 末尾から削除(デフォルト)
item = od.popitem()
print(item) # ('c', 3)
# 先頭から削除
item = od.popitem(last=False)
print(item) # ('a', 1)
順序を考慮した比較
from collections import OrderedDict
od1 = OrderedDict([('a', 1), ('b', 2)])
od2 = OrderedDict([('b', 2), ('a', 1)])
print(od1 == od2) # False(順序も比較される)
⚠️ 注意:OrderedDict と dict の比較
OrderedDict と 通常の dict(他の Mapping) を比較する場合、
順序は無視され、通常の辞書と同じルールで比較されます。
from collections import OrderedDict
od = OrderedDict([('a', 1), ('b', 2)])
d = {'b': 2, 'a': 1}
print(od == d) # True(順序は無視される)
- OrderedDict 同士 → 順序を考慮
- OrderedDict と dict → 順序を無視
この挙動は実務でも混乱しやすいポイントです。
LRUキャッシュの実装例
LRU(Least Recently Used)は、キャッシュが満杯になったときに最も最近使われていないデータから削除する方式です。
from collections import OrderedDict
class LRUCache:
def __init__(self, capacity):
self.cache = OrderedDict()
self.capacity = capacity
def get(self, key):
if key not in self.cache:
return None
self.cache.move_to_end(key)
return self.cache[key]
def put(self, key, value):
if key in self.cache:
self.cache.move_to_end(key)
self.cache[key] = value
if len(self.cache) > self.capacity:
self.cache.popitem(last=False)
cache = LRUCache(3)
cache.put('a', 1)
cache.put('b', 2)
cache.put('c', 3)
cache.get('a')
cache.put('d', 4)
print(list(cache.cache.keys())) # ['c', 'a', 'd']
ChainMap
基本
ChainMap は複数の辞書を コピーせず に、1つの辞書のように扱えるクラスです。
from collections import ChainMap
defaults = {'color': 'red', 'size': 'medium'}
user_settings = {'color': 'blue'}
config = ChainMap(user_settings, defaults)
print(config['color']) # blue
print(config['size']) # medium
検索順序(lookup)と反復順序(iteration)
検索(lookup)
- 先頭 → 末尾
- 最初に見つかった値が採用される
反復(iteration)
- 末尾 → 先頭
- 公式ドキュメントでは「last to first をスキャンして決定」と定義されている
from collections import ChainMap
first = {'a': 1, 'b': 2}
second = {'c': 3, 'd': 4}
chain = ChainMap(first, second)
print(list(chain.keys()))
# ['c', 'd', 'a', 'b']
※ 重複キーがある場合は、lookup と同様に 先頭の辞書の値が優先されます。
maps 属性
from collections import ChainMap
chain = ChainMap({'a': 1}, {'b': 2})
print(chain.maps)
chain['c'] = 3
print(chain.maps)
new_child() と parents
from collections import ChainMap
parent = {'x': 1, 'y': 2}
chain = ChainMap(parent)
child = chain.new_child()
child['x'] = 10
child['z'] = 3
print(child['x']) # 10
print(child['y']) # 2
print(parent) # {'x': 1, 'y': 2}
OrderedDict / dict / ChainMap 比較
| 特徴 | dict | OrderedDict | ChainMap |
|---|---|---|---|
| 挿入順保持 | ◎ | ◎ | △ |
| 順序操作API | × | ◎ | × |
| 比較時に順序考慮 | × | ◎(同士のみ) | × |
| 複数辞書結合 | ○(コピー) | ○(コピー) | ◎(参照) |
| メモリ効率 | ◎ | ○ | ◎ |
※ ChainMap は単一辞書としての挿入順を管理しない
まとめ
-
OrderedDictは 順序を操作したいとき に使う - 比較時の挙動(dict との比較)は要注意
-
ChainMapは スコープ・設定の階層化 に非常に強い - lookup と iteration の順序は必ず分けて理解する
次回は defaultdict の実践的な使い方を扱う予定です。(仮)



