次のような、ネストされた辞書構造を解析するための getattr() のような優れた関数が多数あります。
並列の setattr() を作成したいと思います。基本的に、与えられた:
cmd = 'f[0].a'
val = 'whatever'
x = {"a":"stuff"}
割り当てることができるような関数を作成したいと思います:
x['f'][0]['a'] = val
多かれ少なかれ、これは次と同じように機能します。
setattr(x,'f[0].a',val)
得た:
>>> x
{"a":"stuff","f":[{"a":"whatever"}]}
私は現在それを呼んでいsetByDot()
ます:
setByDot(x,'f[0].a',val)
これに関する 1 つの問題は、中間のキーが存在しない場合、存在しない場合は中間キーを確認して作成する必要があることです。つまり、上記の場合:
>>> x = {"a":"stuff"}
>>> x['f'][0]['a'] = val
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
KeyError: 'f'
したがって、最初に以下を作成する必要があります。
>>> x['f']=[{}]
>>> x
{'a': 'stuff', 'f': [{}]}
>>> x['f'][0]['a']=val
>>> x
{'a': 'stuff', 'f': [{'a': 'whatever'}]}
もう 1 つは、次の項目がリストの場合のキーイングは、次の項目が文字列の場合のキーイングとは異なります。つまり、次のようになります。
>>> x = {"a":"stuff"}
>>> x['f']=['']
>>> x['f'][0]['a']=val
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'str' object does not support item assignment
...割り当てがnull dictではなくnull文字列用だったため、失敗します。null dict は、リストまたは値である最後のものまで、dict 内のすべての非リストの正しい割り当てになります。
以下の @TokenMacGuy のコメントで指摘された 2 番目の問題は、存在しないリストを作成する必要がある場合、非常に多くの空白の値を作成する必要がある場合があることです。そう、
setattr(x,'f[10].a',val)
---アルゴリズムが次のような中間体を作成する必要があることを意味する場合があります。
>>> x['f']=[{},{},{},{},{},{},{},{},{},{},{}]
>>> x['f'][10]['a']=val
得た
>>> x
{"a":"stuff","f":[{},{},{},{},{},{},{},{},{},{},{"a":"whatever"}]}
これがゲッターに関連付けられたセッターであるように...
>>> getByDot(x,"f[10].a")
"whatever"
さらに重要なことに、中間体は既に存在する値を上書き/しない/上書きすべきではありません。
以下は、私がこれまでに持っていた厄介なアイデアです---リストと辞書やその他のデータ型を識別し、それらが存在しない場所に作成できます。ただし、(a) 再帰呼び出しを配置する場所、または (b) リストを反復処理するときにディープ オブジェクトを「構築」する方法、および (c) /probing/ を区別する方法がわかりません。 /setting/ から深いオブジェクトを構築するのと同じように、スタックの最後に到達したときに行う必要があります。
def setByDot(obj,ref,newval):
ref = ref.replace("[",".[")
cmd = ref.split('.')
numkeys = len(cmd)
count = 0
for c in cmd:
count = count+1
while count < numkeys:
if c.find("["):
idstart = c.find("[")
numend = c.find("]")
try:
deep = obj[int(idstart+1:numend-1)]
except:
obj[int(idstart+1:numend-1)] = []
deep = obj[int(idstart+1:numend-1)]
else:
try:
deep = obj[c]
except:
if obj[c] isinstance(dict):
obj[c] = {}
else:
obj[c] = ''
deep = obj[c]
setByDot(deep,c,newval)
プレースホルダーを作成している場合、 /next/ オブジェクトのタイプを確認するために先読みする必要があり、パスを作成するために後読みする必要があるため、これは非常に注意が必要です。
アップデート
私も最近この質問に答えてもらいました。これは関連性があるか、役立つかもしれません。