更具python风格的更改多级字典值的方法
例如,我有下面的格言:更具python风格的更改多级字典值的方法,python,dictionary,nested,Python,Dictionary,Nested,例如,我有下面的格言: {'foo': 'test', 'bar': {'test1': 'some text here'}, 'baz': {'test2': {'test3': 'some text here', 'test4': 'some text here'}}} 或者类似的,可能更高层次 现在的问题是,我想把…'sometext here'改为'A test'。是的,我可以使用大量的进行循环,如以下代码: d = {'foo': 'test', 'bar': {'t
{'foo': 'test',
'bar': {'test1': 'some text here'},
'baz': {'test2': {'test3': 'some text here', 'test4': 'some text here'}}}
或者类似的,可能更高层次
现在的问题是,我想把…'sometext here'
改为'A test'
。是的,我可以使用大量的进行循环,如以下代码:
d = {'foo': 'test',
'bar': {'test1': 'some text here'},
'baz': {'test2': {'test3': 'some text here',
'test4': 'some text here'}}}
for i in d:
if d[i] == 'some text here':
d[i] = 'A test'
elif type(d[i]) == dict:
for j in d[i]:
if d[i][j] == 'some text here':
d[i][j] = 'A test'
elif type(d[i][j]) == dict:
for n in d[i][j]:
if d[i][j][n] == 'some text here':
d[i][j][n] = 'A test'
__import__('pprint').pprint(d)
输出:
{'bar': {'test1': 'A test'},
'baz': {'test2': {'test3': 'A test', 'test4': 'A test'}},
'foo': 'test'}
然而,我认为这不是一个好方法……有什么想法吗?这看起来是递归的好例子
import re
def replace_rec(data, search, replace, _pattern=None):
if _pattern is None:
_pattern = re.compile(r'^%s$' % search)
for k, v in data.items():
try:
data[k] = _pattern.sub(replace, v)
except TypeError:
try:
replace_rec(data[k], search, replace, _pattern=_pattern)
except AttributeError:
# Leave any other types as they are.
continue
将其用于以下示例:
>>> data = {
... 'foo': 'test',
... 'bar': {'test1': 'some text here'},
... 'baz': {'test2': {'test3': 'some text here', 'test4': 'some text here'}},
... 'loc': [1, 2, 3],
... 'fp': 'foo some text here foo',
... }
>>> replace_rec(data, 'some text here', 'A test')
>>> pprint.pprint(data)
{'bar': {'test1': 'A test'},
'baz': {'test2': {'test3': 'A test', 'test4': 'A test'}},
'foo': 'test',
'fp': 'foo some text here foo',
'loc': [1, 2, 3]}
略为另类的版本。这将正确处理字典中的非字符串,并且只替换精确的文本匹配
def replace(d, find_text, replace_text):
for k, v in d.items():
if isinstance(v, dict):
replace(v, find_text, replace_text)
elif isinstance(v, str):
if v == find_text:
d[k] = replace_text
d = {
'test': 'dont change some text here',
'ignore' : 42,
'foo': 'test',
'bar': {'test1': 'some text here'},
'baz': {'test2': {'test3': 'some text here', 'test4': 'some text here'}}}
replace(d, 'some text here', 'A test')
__import__('pprint').pprint(d)
这将显示:
{'bar': {'test1': 'A test'},
'baz': {'test2': {'test3': 'A test', 'test4': 'A test'}},
'foo': 'test',
'ignore': 42,
'test': 'dont change some text here'}
递归答案都是有趣的游戏,但是你可以通过注意字典是可变的来获得更多的Pythonic:
首先获取所有字典的引用(在所有级别),然后更改它们
def all_dicts(d):
yield d
for v in d.values():
if isinstance(v, dict):
yield from all_dicts(v)
data = dict(foo=12, bar=dict(dd=12), ex=dict(a=dict(b=dict(v=12))))
for d in all_dicts(data):
for k, v in d.items():
if v == 12:
d[k] = 33
只要您正在更改的值不太复杂,这里有一个简单的说明:
In [29]: data = dict(foo=12, bar=dict(dd=12), ex=dict(a=dict(b=dict(v=12))))
In [30]: json.loads(re.sub("12", "33", json.dumps(data)))
Out[30]: {'bar': {'dd': 33}, 'ex': {'a': {'b': {'v': 33}}}, 'foo': 33}
编辑,根据Kevin的说法,简单的替换甚至不需要re
:
json.loads(json.dumps(data).replace("12", "33"))
@user2393267:这叫做标签,而不是标签:)替换的标准是什么,在任何深度上精确匹配任何值?@qarma:嗯,实际上我在我的问题中说过:可能更多层次。嗯,我认为你的答案和justinfay之前接受的答案没有太大区别。所以他的答案有一个关于非字符串的问题,也许一个注释会比另一个答案更好。他的答案也会替换为“不要在这里更改一些文本”。你的问题不清楚这个案子是否也需要更换。嗯……明白,他的回答可以解决我的问题,这就足够了。如果我的口述更复杂,你的问题会更有用。正确的?我对你的答案投了更高的票。你可能应该编辑我的答案,包括检查它是否是字符串,因为你的函数的结构完全相同。好吧…为什么我没有尝试这个…太好了!顺便说一句,两支安打:1。如果键中有12
,这也将替换键,可能使用':33'
而不是'33'
?2.为什么不直接使用str.replace()
?很好,.replace()
甚至更好!只要值足够唯一。我专门为可能的特殊情况使用了re
,比如键。如果你想以这种方式强制使用它,那么当str和eval可以做你想做的事情时,为什么还要用json呢eval(str({foo':33})。替换('33',11'))
@justinfay,因为eval
是邪恶的,因为\uuuu repr\uuuuu
表示产生不一致的引号['Jeanne','d'Arc]