下記の記事の続きです。自分でも続きがあったのかと思っておりますが、今回は、前回と同じ要領で、あるdictについて、特定のプロパティ(複数可能)を抜き出したdictを得るというミニDSL風の関数を作成しました(以下 hoge2.py)。
なお、hoge2.pyを作成してて、もう一声加えるとPython 3.9で導入されるネストされたdictのマージ演算子相当に近づけられそうだったので、これも自分メモ・活動の記録としておまけでつけてあります(以下hoge_merge.py)。
■(参考リンク)Python3.9のdictに関するマージ演算子「|」
抜き出し
■hoge2.py(Pythonです)
selectfieldsというのが該当のもの。 なお、selectfieldsの第2引数で、抜き出したいプロパティを示すリストを指定するが、お行儀の良いものがお行儀の良い順で指定されることとする。例えば、「a.b.c」の後に「a.b」が指定される場合は、「a.b」が抜き出されてしまうことから、「a.b.f」などが成り行き保持されてしまい、「a.b.c」のみ選択したいという指定は機能しない。
dx = { "a": {"b": {"c": "あああ", "d": ["いいい"], "f": "えええ"}}, "x": "ううう" } def selectfields(origobj, _paths): paths = copy.deepcopy(_paths) #初出時「_pathsを破壊的に動作する挙動」であったが、破壊しないように見直し(2020/12/1) obj_ = {} def _setobj(obj, path, up=[]): c = path.pop(0) p = up + [c] if len(path) == 0: #findfield関数は前回の記事のものと同じ if _ := findfield(origobj, p): obj[c] = _ return if c not in obj.keys(): obj[c] = {} _setobj(obj[c], path, p) for path in paths: _setobj(obj_, path) return obj_ # dxのa.b.d、a.b.fを抜き出す。ちなみに、「a.x」は存在しないので、空振りする。 dx_ = selectfields( dx, ['a.x'.split('.'), 'a.b.d'.split('.'), 'a.b.f'.split('.')]) print(dx_)
■hoge2.pyの実行結果
{'a': {'b': {'d': ['いいい'], 'f': 'えええ'}}}
マージ
getpathsで、あるdictのすべてのプロパティを「a.b.c〜」のリストで取得し、先述の「_setobj」を使って、マージ先のdictにコピーする(merge関数)。
■hoge_merge.py
def getpaths(obj): pathslist = [] def _getpaths(obj, up=[]): for k in obj.keys(): p = up + [k] if isinstance(obj[k], dict): # dictなら掘り下げる _getpaths(obj[k], p) else: # 行き止まりであれば、pathを表すリストを出力する pathslist.append(p) _getpaths(obj) return pathslist print("getpathsの挙動") print(getpaths(dx)) def merge(obj1, obj2): obj_ = copy.deepcopy(obj1) origobj = obj2 # この_setobjは上述の「hoge2.py」のものと同じ def _setobj(obj, path, up=[]): c = path.pop(0) p = up + [c] if len(path) == 0: if _ := findfield(origobj, p): obj[c] = _ return if c not in obj.keys(): obj[c] = {} _setobj(obj[c], path, p) for path in getpaths(obj2): _setobj(obj_, path) return obj_ dy = {"a": {"y": "わい"}, "z": "ぜっと"} dx = { "a": {"b": {"c": "あああ", "d": ["いいい"], "f": "えええ", "g": {"h": "えいち"}}}, "x": "ううう" } print("merge(dy, dx)の挙動") print(merge(dy, dx))
■hoge_merge.pyの実行結果
getpathsの挙動 [['a', 'b', 'c'], ['a', 'b', 'd'], ['a', 'b', 'f'], ['a', 'b', 'g', 'h'], ['x']] merge(dy, dx)の挙動 {'a': {'y': 'わい', 'b': {'c': 'あああ', 'd': ['いいい'], 'f': 'えええ', 'g': {'h': 'えいち'}}}, 'z': 'ぜっと', 'x': 'ううう'}
テストパターンが少ないので見逃しがあるかもしれない*1。 また、今回やりたかったことについては、問題をうまく分解できた気がしていますが、一方、効率的かというとそうではないかもしれません。
*1:もともと割り切り仕様になっているところはありますし...