glomを使ってdictのネストを取り扱ってみた
今回はPythonのライブラリであるglomを使ってみました。glomを利用するとネストされたdictを扱う際にいくつもキーにアクセスするためにカッコを使わなくても簡単にアクセスできるようになります。
ちなみに、なぜこのライブラリを知ったかというと以下の記事が気になって読んでいると出てきたので調べてみた次第です。ぜひ以下の記事もご覧ください。
glomとは?
glomはPythonのデータを取り扱うための新しいアプローチを提供してくれます。具体的には以下の特徴があるようです。
- ネスト構造のパスベースでのアクセス
- 軽量かつPythonらしい仕様を用いた宣言的なデータ変換
- 読みやすく意味のあるエラーメッセージ
- 組み込みのデバッグ機能
- ディープアサインメントやストリーミング、データ検証などの様々な機能を備えています。
より具体的な説明はぜひ公式ドキュメントをご覧ください。
早速使ってみる
環境構築
uvを利用して環境を構築しました。
uv init glob_tutorial -p 3.12
cd glob_tutorial
uv add glom
サンプルの実行
それではまずはサンプルを実行してみましょう。ここではまずネストされたdictから深い場所にあるデータを取り出すコードの実装になります。
from glom import glom
target = {'a': {'b': {'c': 'Hello, World!'}}}
# Normal Access
print(target["a"]["b"]["c"])
# Using glom
print(glom(target, 'a.b.c'))
まず以下のように複数段階にネストされた辞書を用意します。
target = {'a': {'b': {'c': 'Hello, World!'}}}
例えばこのような辞書が会った時に、一番ふかいcと言うキーにアクセスしてHello World!が欲しい場合、以下のように呼び出すことが多いと思います。
print(target["a"]["b"]["c"])
これをglomを使うと以下のようにドットで区切ったパスのように扱うことでアクセスすることができます。glob.glob(target_data, "access_path")のように指定することで参照できます。
print(glom(target, 'a.b.c'))
早速このコードを動かしてみましょう。
uv run nest_dict.py
# 結果
Hello, World!
Hello, World!
結果をみると全く同じ結果になっています。どちらの方がアクセスしやすいかでいうと文字量こそほとんど違いはないものの、いくつもかっこでネストするよりはドット区切りでキーを指定する方が個人的には好みです。
ワイルドカードを使った検索
他にも様々な機能がありますが、例えばワイルドカードを用いた検索もできます。例えばa.b1.c1とa.b2.c1のように中間地点でb1、b2のように違いはあるものの最終目標のパスが同じ場合などに、a.*.c1のようにすることでまとめて結果を得られると言うものです。例えばこの例を実現したい場合以下のようなコードになります。
from glom import glom
target = {
"a": {
"b1": {
"c1": "b1_c1", "c2": "b1_c2"
},
"b2": {
"c1": "b2_c1", "c2": "b2_c2"
},
}
}
print(glom(target, {"c1": "a.*.c1", "c2": "a.*.c2"}))
この例ではa.b1.c1とa.b2.c1、a.b1.c2とa.b2.c2をひとまとまりとして取得できるようにしています。また、glomのパスの引数として以下のように辞書として登録することで、検索結果を該当するキーに対応する値として生成できます。
{"c1": "a.*.c1", "c2": "a.*.c2"}
これを実行すると以下のようになります。
uv run nest_dict_widecard.py
# 結果
{'c1': ['b1_c1', 'b2_c1'], 'c2': ['b1_c2', 'b2_c2']}
結果を見るとc1とc2でそれぞれグループ化された結果を取得できています。
まとめ
今回はglomについて紹介しました。glomを使うとネストされた辞書ないの検索をより簡潔にアクセスできるだけでなく、同じパターンに対する結果をまとめて結果として集約できるのも特徴です。公式ドキュメントにはこの他多数の機能が提供されているとのことなのでぜひ試してみてください。
Discussion