note.nkmk.me

Pythonの順序付き辞書OrderedDictの使い方

Date: 2016-12-08 / Modified: 2018-05-16 / tags: Python, 辞書
このエントリーをはてなブックマークに追加

Pythonの辞書(dict型オブジェクト)は要素の順番を保持しない。CPythonは3.6から順番を保持しているが、実装依存なのでそのほかの実装では不定。3.7から言語仕様で順番を保持するようになる(らしい)。

標準ライブラリのcollectionsモジュールに順番が保持された辞書としてOrderedDictが用意されている。こちらを使っておけば安心。

collectionsモジュールをインポートする。標準ライブラリに含まれているのでインストールする必要はない。

import collections

以下のように書けば、以降の例のcollections.は省略できる。

from collections import OrderedDict

OrderedDictの使い方として以下の内容を説明する。

  • OrderedDictオブジェクトの作成
  • OrderedDictはdictのサブクラス
  • 要素を先頭・末尾に移動
  • 任意の位置に新たな要素を追加
  • 要素を入れ替え(並べ替え)
  • 要素をキーまたは値でソート
スポンサーリンク

OrderedDictオブジェクトの作成

コンストラクタcollections.OrderedDict()OrderedDictオブジェクトを作成できる。

空のOrderedDictオブジェクトを作成して値を追加。

od = collections.OrderedDict()

od['k1'] = 1
od['k2'] = 2
od['k3'] = 3

print(od)
# OrderedDict([('k1', 1), ('k2', 2), ('k3', 3)])

コンストラクタに引数を指定することも可能。

キーワード引数や、キーと値のペアのシーケンス(タプル(key, value)など)のシーケンスなどが使える。後者はキーと値のペアであればリストでもタプルでもOK。

print(collections.OrderedDict(k1=1, k2=2, k3=3))
print(collections.OrderedDict([('k1', 1), ('k2', 2), ('k3', 3)]))
print(collections.OrderedDict((['k1', 1], ['k2', 2], ['k3', 3])))
# OrderedDict([('k1', 1), ('k2', 2), ('k3', 3)])
# OrderedDict([('k1', 1), ('k2', 2), ('k3', 3)])
# OrderedDict([('k1', 1), ('k2', 2), ('k3', 3)])

バージョン3.5まではキーワード引数だと順序が保持されていなかったが、バージョン3.6から保持されるようになった。

バージョン 3.6 で変更: PEP 468 の受理によって、OrderedDict のコンストラクタと、update() メソッドに渡したキーワード引数の順序は保持されます。
8.3. collections — コンテナデータ型 — Python 3.6.5 ドキュメント

通常の辞書(dict型オブジェクト)もコンストラクタに渡せるが、dict型が順序を保持していない実装の場合、それから生成したOrderedDictも順序を保持しない。

print(collections.OrderedDict({'k1': 1, 'k2': 2, 'k3': 3}))
# OrderedDict([('k1', 1), ('k2', 2), ('k3', 3)])

OrderedDictはdictのサブクラス

OrderedDictdictのサブクラス。

print(issubclass(collections.OrderedDict, dict))
# True

OrderedDictdictと同じメソッドを持っており、要素の取得や変更、追加、削除などの方法はdictと同じ。

print(od['k1'])
# 1

od['k2'] = 200
print(od)
# OrderedDict([('k1', 1), ('k2', 200), ('k3', 3)])

od.update(k4=4, k5=5)
print(od)
# OrderedDict([('k1', 1), ('k2', 200), ('k3', 3), ('k4', 4), ('k5', 5)])

del od['k4'], od['k5']
print(od)
# OrderedDict([('k1', 1), ('k2', 200), ('k3', 3)])

詳細は以下の記事を参照。

要素を先頭・末尾に移動

OrderedDict独自のメソッドmove_to_end()を使って要素を先頭または末尾に移動できる。

キーを第一引数に指定する。デフォルトは末尾に移動、第二引数lastFalseとすると先頭に移動される。

od.move_to_end('k1')
print(od)
# OrderedDict([('k2', 200), ('k3', 3), ('k1', 1)])

od.move_to_end('k1', False)
print(od)
# OrderedDict([('k1', 1), ('k2', 200), ('k3', 3)])

任意の位置に新たな要素を追加

任意の位置に新たな要素を追加したOrderedDictオブジェクトを新たに作成することができる。

  • items()メソッドで取得できるビューオブジェクト をlist()でリスト化
  • リストのinsert()メソッドでキーと値のペアのタプル(key, value)を追加
  • コンストラクタcollections.OrderedDict()に渡し、新たなオブジェクトを作成

という流れ。

l = list(od.items())
print(l)
# [('k1', 1), ('k2', 200), ('k3', 3)]

l.insert(1, ('kx', -1))
print(l)
# [('k1', 1), ('kx', -1), ('k2', 200), ('k3', 3)]

od = collections.OrderedDict(l)
print(od)
# OrderedDict([('k1', 1), ('kx', -1), ('k2', 200), ('k3', 3)])

insert()は第一引数に挿入する位置、第二引数に挿入する要素を指定する。

例では元の変数に新たなオブジェクトを代入しており、元のオブジェクト自体に新たな要素を追加したわけではない。

要素を入れ替え(並べ替え)

要素の入れ替えも上の例と同じ流れ。

  • items()メソッドで取得できるビューオブジェクト をlist()でリスト化
  • リストの要素を入れ替え
  • コンストラクタcollections.OrderedDict()に渡し、新たなオブジェクトを作成
l = list(od.items())
print(l)
# [('k1', 1), ('kx', -1), ('k2', 200), ('k3', 3)]

l[0], l[2] = l[2], l[0]
print(l)
# [('k2', 200), ('kx', -1), ('k1', 1), ('k3', 3)]

od = collections.OrderedDict(l)
print(od)
# OrderedDict([('k2', 200), ('kx', -1), ('k1', 1), ('k3', 3)])

キーを指定して入れ替えたい場合は、以下のようにキーのリストからindex()メソッドでインデックス(位置)を位置を取得する。

l = list(od.items())
k = list(od.keys())
print(k)
# ['k2', 'kx', 'k1', 'k3']

print(k.index('kx'))
# 1

l[k.index('kx')], l[k.index('k3')] = l[k.index('k3')], l[k.index('kx')]
print(l)
# [('k2', 200), ('k3', 3), ('k1', 1), ('kx', -1)]

od = collections.OrderedDict(l)
print(od)
# OrderedDict([('k2', 200), ('k3', 3), ('k1', 1), ('kx', -1)])

もっといいやり方がありそうな気もする。

要素をキーまたは値でソート

items()メソッドで取得できるビューオブジェクトをもとにソートしたキーと値のペアのタプル(key, value)のリストを作成、コンストラクタcollections.OrderedDict()に渡し、新たなオブジェクトを作成する。

組み込み関数sorted()の引数keyにタプル(key, value)からキーまたは値を返す無名関数(ラムダ式)を指定してソートする。

逆順にする場合はsorted()の引数reverseTrueにする。

print(od)
# OrderedDict([('k2', 200), ('k3', 3), ('k1', 1), ('kx', -1)])

od_sorted_key = collections.OrderedDict(
    sorted(od.items(), key=lambda x: x[0])
)
print(od_sorted_key)
# OrderedDict([('k1', 1), ('k2', 200), ('k3', 3), ('kx', -1)])

od_sorted_value = collections.OrderedDict(
    sorted(od.items(), key=lambda x: x[1], reverse=True)
)
print(od_sorted_value)
# OrderedDict([('k2', 200), ('k3', 3), ('k1', 1), ('kx', -1)])
スポンサーリンク
シェア
このエントリーをはてなブックマークに追加

関連カテゴリー

関連記事