LoginSignup
3
6

More than 5 years have passed since last update.

python3のcollectionsモジュール(ChainMap)を使ってみる

Posted at

collections

collectionsはコンテナデータ型が入ってるものです。
python3(3.4.3)ではpython2.7のものに幾つか追加されたものがあります。
参考URL: http://docs.python.jp/3.4/library/collections.html
python2.7とから追加された点は、下記のものです。

ChainMap: 複数のマッピングの一つのビューを作成する辞書風のクラス
UserDict: 辞書のサブクラス化を簡単にする辞書オブジェクトのラッパ
UserList: リストのサブクラス化を簡単にするリストオブジェクトのラッパ
UserString: 文字列のサブクラス化を簡単にする文字列オブジェクトのラッパ

User〜は普通にクラスラッパなので説明は省略します。

ChainMap

基本

今回書いたコードはGitHubにあります。(https://github.com/KodairaTomonori/Qiita/blob/master/default_module/collections/test_ChainMap.py)
まず、ChainMapの簡単な使い方から、

test_ChainMap.py
import collections
import see
dict_a = dict(a=1, b=2, c=3)
dict_b = dict(x=10, y=11, z=12)
def get_name(arg_id, arg_values):
    for key, value in arg_values.items():
        if id(value) == arg_id: return key
    return 'NotDefined'

def print_arguments(*args, arg_values):
    for arg in args:
        print(get_name(id(arg), arg_values) + ' : ' + repr(arg) )

chain_map = collections.ChainMap(dict_a, dict_b)
child = chain_map.new_child()
print('make_chain_maps')
print_arguments(chain_map, child, arg_values=locals() )
print('chain_map.maps: ' + repr(chain_map.maps) )

出力

make_chain_maps
chain_map : ChainMap({'b': 2, 'c': 3, 'a': 1}, {'z': 12, 'x': 10, 'y': 11})
child : ChainMap({}, {'b': 2, 'c': 3, 'a': 1}, {'z': 12, 'x': 10, 'y': 11})
chain_map.maps: [{'a': 1, 'c': 3, 'b': 2}, {'y': 11, 'z': 12, 'x': 10}]

説明

ChainMapは名前の通りmap(dictionaly)をつなげるものです。
ただ単につなげるのではなく、それぞれを一つの辞書として保持しておけます。
なので、出力を見てわかる通りdict_a, dict_bそれぞれが個別にあります。
chain['z'], chain_map['b']とするとそれぞれ、'2, 12'がでてきます。
使うときは一つのdictとして普通に使えます。
.new_child()とすることで、先頭に新しいdictを作ってくれます。
.mapsとすることで、ChainMapをリストして返してくれます。

コードには書いてませんが、普通のdictと同様に.items(), .keys(), .values()が使えます。
参考URLを見ると、探索は手前から、その他は一番手前のみって感じですね

探索は、根底のマッピングをキーが見つかるまで引き続き探します。対して、書き込み、更新、削除は、最初のマッピングのみ操作します。

子と親の更新

次に、更新をやっていきます。

update.py
child.update(d=4, a=0)
print('update_child_map')
print_arguments(chain_map, child, arg_values=locals() )
print()

chain_map.update(z=100)
print('update_parent_map')
print_arguments(chain_map, child, arg_values=locals() )

出力2

update_child_map
chain_map : ChainMap({'a': 1, 'c': 3, 'b': 2}, {'y': 11, 'z': 12, 'x': 10})
child : ChainMap({'a': 0, 'd': 4}, {'a': 1, 'c': 3, 'b': 2}, {'y': 11, 'z': 12, 'x': 10})
update_parent_map
chain_map : ChainMap({'a': 1, 'z': 100, 'c': 3, 'b': 2}, {'y': 11, 'z': 12, 'x': 10})
child : ChainMap({'a': 0, 'd': 4}, {'a': 1, 'z': 100, 'c': 3, 'b': 2}, {'y': 11, 'z': 12, 'x': 10})

説明2

子(一番左のdict)に書き込みor更新をしても、親(一番左以外のdict)には変更がありません。
また、親を変更すると、'child'のほうにもその変更は反映されています。
chain_map, childのそれぞれの共通要素のpointerは一緒のところをさしてる感じになってます。

マージ

なんかごちゃごちゃして見にくいときに一つの辞書にする方法。
単純にdict()で囲ってあげるだけでできます。

merge.py
print('each_Chain')
print('chain_map:', dict(**chain_map) )
print('child    :', dict(**child) )

出力

each_Chain
chain_map: {'x': 10, 'z': 100, 'c': 3, 'b': 2, 'a': 1, 'y': 11}
child : {'x': 10, 'z': 100, 'c': 3, 'b': 2, 'a': 0, 'd': 4, 'y': 11}

まとめ

ChainMapは便利ですがあんまり使い道が思いつきません。

このクラスはネストされたスコープをシミュレートするのに使え、テンプレート化に便利です。

と参考URLには書いてありますが、カタカナばっかでよくわかりません。
NLPとかで学習する際の重みの更新とかを確認するのに使えそうですね。

3
6
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
6