有很多很好的 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)
这样做的一个问题是,如果中间的键不存在,则需要检查并制作中间键(如果它不存在)——即,对于上述情况:
>>> 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'}]}
另一个是下一项是列表时的键控将不同于下一项是字符串时的键控,即:
>>> 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 将是 dict 中每个非列表的正确分配,直到最后一个——它可能是一个列表或一个值。
@TokenMacGuy 在下面的评论中指出的第二个问题是,当您必须创建一个不存在的列表时,您可能必须创建大量空白值。所以,
setattr(x,'f[10].a',val)
---可能意味着算法将不得不制作一个中间体,如:
>>> x['f']=[{},{},{},{},{},{},{},{},{},{},{}]
>>> x['f'][10]['a']=val
屈服
>>> x
{"a":"stuff","f":[{},{},{},{},{},{},{},{},{},{},{"a":"whatever"}]}
这样这是与 getter 关联的 setter ...
>>> 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/ 对象的类型,并且您必须向后看以构建路径。
更新
我最近也回答了这个问题,这可能是相关的或有帮助的。